From 4b3b1fec4e57bd07ac75700bf34cd188ce43b545 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 15 Apr 2020 21:19:43 +0300 Subject: added an endpoint for getting unread notification count --- lib/pleroma/marker.ex | 45 +++++++++++++++++++++- lib/pleroma/notification.ex | 47 ++++++++++++++++------- lib/pleroma/web/mastodon_api/views/marker_view.ex | 5 ++- 3 files changed, 82 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex index 443927392..4d82860f5 100644 --- a/lib/pleroma/marker.ex +++ b/lib/pleroma/marker.ex @@ -9,24 +9,34 @@ defmodule Pleroma.Marker do import Ecto.Query alias Ecto.Multi + alias Pleroma.Notification alias Pleroma.Repo alias Pleroma.User + alias __MODULE__ @timelines ["notifications"] + @type t :: %__MODULE__{} schema "markers" do field(:last_read_id, :string, default: "") field(:timeline, :string, default: "") field(:lock_version, :integer, default: 0) + field(:unread_count, :integer, default: 0, virtual: true) belongs_to(:user, User, type: FlakeId.Ecto.CompatType) timestamps() end + @doc "Gets markers by user and timeline." + @spec get_markers(User.t(), list(String)) :: list(t()) def get_markers(user, timelines \\ []) do - Repo.all(get_query(user, timelines)) + user + |> get_query(timelines) + |> unread_count_query() + |> Repo.all() end + @spec upsert(User.t(), map()) :: {:ok | :error, any()} def upsert(%User{} = user, attrs) do attrs |> Map.take(@timelines) @@ -45,6 +55,27 @@ defmodule Pleroma.Marker do |> Repo.transaction() end + @spec multi_set_last_read_id(Multi.t(), User.t(), String.t()) :: Multi.t() + def multi_set_last_read_id(multi, %User{} = user, "notifications") do + multi + |> Multi.run(:counters, fn _repo, _changes -> + {:ok, %{last_read_id: Repo.one(Notification.last_read_query(user))}} + end) + |> Multi.insert( + :marker, + fn %{counters: attrs} -> + %Marker{timeline: "notifications", user_id: user.id} + |> struct(attrs) + |> Ecto.Changeset.change() + end, + returning: true, + on_conflict: {:replace, [:last_read_id]}, + conflict_target: [:user_id, :timeline] + ) + end + + def multi_set_last_read_id(multi, _, _), do: multi + defp get_marker(user, timeline) do case Repo.find_resource(get_query(user, timeline)) do {:ok, marker} -> %__MODULE__{marker | user: user} @@ -71,4 +102,16 @@ defmodule Pleroma.Marker do |> by_user_id(user.id) |> by_timeline(timelines) end + + defp unread_count_query(query) do + from( + q in query, + left_join: n in "notifications", + on: n.user_id == q.user_id and n.seen == false, + group_by: [:id], + select_merge: %{ + unread_count: fragment("count(?)", n.id) + } + ) + end end diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 04ee510b9..3084bac3b 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -5,7 +5,9 @@ defmodule Pleroma.Notification do use Ecto.Schema + alias Ecto.Multi alias Pleroma.Activity + alias Pleroma.Marker alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Pagination @@ -38,6 +40,17 @@ defmodule Pleroma.Notification do |> cast(attrs, [:seen]) end + @spec last_read_query(User.t()) :: Ecto.Queryable.t() + def last_read_query(user) do + from(q in Pleroma.Notification, + where: q.user_id == ^user.id, + where: q.seen == true, + select: type(q.id, :string), + limit: 1, + order_by: [desc: :id] + ) + end + defp for_user_query_ap_id_opts(user, opts) do ap_id_relationships = [:block] ++ @@ -186,25 +199,23 @@ defmodule Pleroma.Notification do |> Repo.all() end - def set_read_up_to(%{id: user_id} = _user, id) do + def set_read_up_to(%{id: user_id} = user, id) do query = from( n in Notification, where: n.user_id == ^user_id, where: n.id <= ^id, where: n.seen == false, - update: [ - set: [ - seen: true, - updated_at: ^NaiveDateTime.utc_now() - ] - ], # Ideally we would preload object and activities here # but Ecto does not support preloads in update_all select: n.id ) - {_, notification_ids} = Repo.update_all(query, []) + {:ok, %{ids: {_, notification_ids}}} = + Multi.new() + |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()]) + |> Marker.multi_set_last_read_id(user, "notifications") + |> Repo.transaction() Notification |> where([n], n.id in ^notification_ids) @@ -221,11 +232,18 @@ defmodule Pleroma.Notification do |> Repo.all() end + @spec read_one(User.t(), String.t()) :: + {:ok, Notification.t()} | {:error, Ecto.Changeset.t()} | nil def read_one(%User{} = user, notification_id) do with {:ok, %Notification{} = notification} <- get(user, notification_id) do - notification - |> changeset(%{seen: true}) - |> Repo.update() + Multi.new() + |> Multi.update(:update, changeset(notification, %{seen: true})) + |> Marker.multi_set_last_read_id(user, "notifications") + |> Repo.transaction() + |> case do + {:ok, %{update: notification}} -> {:ok, notification} + {:error, :update, changeset, _} -> {:error, changeset} + end end end @@ -307,8 +325,11 @@ defmodule Pleroma.Notification do # TODO move to sql, too. def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do unless skip?(activity, user) do - notification = %Notification{user_id: user.id, activity: activity} - {:ok, notification} = Repo.insert(notification) + {:ok, %{notification: notification}} = + Multi.new() + |> Multi.insert(:notification, %Notification{user_id: user.id, activity: activity}) + |> Marker.multi_set_last_read_id(user, "notifications") + |> Repo.transaction() if do_send do Streamer.stream(["user", "user:notification"], notification) diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex index 985368fe5..415dae93b 100644 --- a/lib/pleroma/web/mastodon_api/views/marker_view.ex +++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex @@ -10,7 +10,10 @@ defmodule Pleroma.Web.MastodonAPI.MarkerView do Map.put_new(acc, m.timeline, %{ last_read_id: m.last_read_id, version: m.lock_version, - updated_at: NaiveDateTime.to_iso8601(m.updated_at) + updated_at: NaiveDateTime.to_iso8601(m.updated_at), + pleroma: %{ + unread_count: m.unread_count + } }) end) end -- cgit v1.2.3 From 6400998820084c7b81a53bbeb705b0eb2c0a0e1b Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 10:12:37 +0200 Subject: AP C2S: Restrict creation to `Note`s for now. --- lib/pleroma/web/activity_pub/activity_pub_controller.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index f607931ab..504eed4f4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -396,7 +396,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do |> json(err) end - defp handle_user_activity(%User{} = user, %{"type" => "Create"} = params) do + defp handle_user_activity( + %User{} = user, + %{"type" => "Create", "object" => %{"type" => "Note"}} = params + ) do object = params["object"] |> Map.merge(Map.take(params, ["to", "cc"])) -- cgit v1.2.3 From ad771546d886171ea8c3e7694fad393eaa5a2017 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 12:11:46 +0200 Subject: Transmogrifier: Move emoji reactions to common pipeline. --- lib/pleroma/web/activity_pub/builder.ex | 12 ++++ lib/pleroma/web/activity_pub/object_validator.ex | 11 +++ .../object_validators/emoji_react_validator.ex | 81 ++++++++++++++++++++++ lib/pleroma/web/activity_pub/side_effects.ex | 12 ++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 23 +----- 5 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 429a510b8..2a763645c 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -10,6 +10,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()} + def emoji_react(actor, object, emoji) do + with {:ok, data, meta} <- like(actor, object) do + data = + data + |> Map.put("content", emoji) + |> Map.put("type", "EmojiReact") + + {:ok, data, meta} + end + end + @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()} def like(actor, object) do object_actor = User.get_cached_by_ap_id(object.data["actor"]) diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index dc4bce059..8246558ed 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) @@ -24,6 +25,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end + def validate(%{"type" => "EmojiReact"} = object, meta) do + with {:ok, object} <- + object + |> EmojiReactValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object |> Map.from_struct()) + {:ok, object, meta} + end + end + def stringify_keys(object) do object |> Map.new(fn {key, val} -> {to_string(key), val} 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 new file mode 100644 index 000000000..e87519c59 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -0,0 +1,81 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do + use Ecto.Schema + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ObjectValidators.Types + + import Ecto.Changeset + import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + + @primary_key false + + embedded_schema do + field(:id, Types.ObjectID, primary_key: true) + field(:type, :string) + field(:object, Types.ObjectID) + field(:actor, Types.ObjectID) + field(:context, :string) + field(:content, :string) + field(:to, {:array, :string}, default: []) + field(:cc, {:array, :string}, default: []) + end + + def cast_and_validate(data) do + data + |> cast_data() + |> validate_data() + end + + def cast_data(data) do + %__MODULE__{} + |> changeset(data) + end + + def changeset(struct, data) do + struct + |> cast(data, __schema__(:fields)) + |> fix_after_cast() + end + + def fix_after_cast(cng) do + cng + |> fix_context() + end + + def fix_context(cng) do + object = get_field(cng, :object) + + with nil <- get_field(cng, :context), + %Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do + cng + |> put_change(:context, context) + else + _ -> + cng + end + end + + def validate_emoji(cng) do + content = get_field(cng, :content) + + if Pleroma.Emoji.is_unicode_emoji?(content) do + cng + else + cng + |> add_error(:content, "must be a single character emoji") + end + end + + def validate_data(data_cng) do + data_cng + |> validate_inclusion(:type, ["EmojiReact"]) + |> validate_required([:id, :type, :object, :actor, :context, :to, :cc, :content]) + |> validate_actor_presence() + |> validate_object_presence() + |> validate_emoji() + end +end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 6a8f1af96..b15343c07 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -23,6 +23,18 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end + # Tasks this handles: + # - Add reaction to object + # - Set up notification + def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do + reacted_object = Object.get_by_ap_id(object.data["object"]) + Utils.add_emoji_reaction_to_object(object, reacted_object) + + Notification.create_notifications(object) + + {:ok, object, meta} + end + # Nothing to do def handle(object, meta) do {:ok, object, meta} diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 581e7040b..81e763f88 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -656,7 +656,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> handle_incoming(options) end - def handle_incoming(%{"type" => "Like"} = data, _options) do + def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do with :ok <- ObjectValidator.fetch_actor_and_object(data), {:ok, activity, _meta} <- Pipeline.common_pipeline(data, local: false) do @@ -666,27 +666,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - def handle_incoming( - %{ - "type" => "EmojiReact", - "object" => object_id, - "actor" => _actor, - "id" => id, - "content" => emoji - } = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- get_obj_helper(object_id), - {:ok, activity, _object} <- - ActivityPub.react_with_emoji(actor, object, emoji, activity_id: id, local: false) do - {:ok, activity} - else - _e -> :error - end - end - def handle_incoming( %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data, _options -- cgit v1.2.3 From db55dc944581b1d4b50d1608b2e991050ea29bb3 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 12:28:28 +0200 Subject: ActivityPub: Remove `react_with_emoji`. --- lib/pleroma/web/activity_pub/activity_pub.ex | 24 ---------------------- lib/pleroma/web/activity_pub/object_validator.ex | 2 +- lib/pleroma/web/common_api/common_api.ex | 6 ++++-- .../controllers/pleroma_api_controller.ex | 2 +- 4 files changed, 6 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1c21d78af..4c6ac9241 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -349,30 +349,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - @spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) :: - {:ok, Activity.t(), Object.t()} | {:error, any()} - def react_with_emoji(user, object, emoji, options \\ []) do - with {:ok, result} <- - Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do - result - end - end - - defp do_react_with_emoji(user, object, emoji, options) do - with local <- Keyword.get(options, :local, true), - activity_id <- Keyword.get(options, :activity_id, nil), - true <- Pleroma.Emoji.is_unicode_emoji?(emoji), - reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id), - {:ok, activity} <- insert(reaction_data, local), - {:ok, object} <- add_emoji_reaction_to_object(activity, object), - :ok <- maybe_federate(activity) do - {:ok, activity, object} - else - false -> {:error, false} - {:error, error} -> Repo.rollback(error) - end - end - @spec unreact_with_emoji(User.t(), String.t(), keyword()) :: {:ok, Activity.t(), Object.t()} | {:error, any()} def unreact_with_emoji(user, reaction_id, options \\ []) do diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 8246558ed..d730cb062 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -11,8 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Object alias Pleroma.User - alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index f9db97d24..192c84eda 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -177,8 +177,10 @@ defmodule Pleroma.Web.CommonAPI do def react_with_emoji(id, user, emoji) do with %Activity{} = activity <- Activity.get_by_id(id), - object <- Object.normalize(activity) do - ActivityPub.react_with_emoji(user, object, emoji) + object <- Object.normalize(activity), + {:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji), + {:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do + {:ok, activity} else _ -> {:error, dgettext("errors", "Could not add reaction emoji")} diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 1bdb3aa4d..6688d7caf 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -86,7 +86,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do end def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do - with {:ok, _activity, _object} <- CommonAPI.react_with_emoji(activity_id, user, emoji), + with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji), activity <- Activity.get_by_id(activity_id) do conn |> put_view(StatusView) -- cgit v1.2.3 From f1da8882f971f932b65f655b6457759387dafe51 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 14:17:47 +0200 Subject: UndoValidator: Add UndoValidator. --- lib/pleroma/web/activity_pub/builder.ex | 13 +++++ lib/pleroma/web/activity_pub/object_validator.ex | 9 ++++ .../object_validators/common_validations.ex | 3 +- .../object_validators/undo_validator.ex | 62 ++++++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/web/activity_pub/object_validators/undo_validator.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 429a510b8..380d8f565 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -10,6 +10,19 @@ defmodule Pleroma.Web.ActivityPub.Builder do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()} + def undo(actor, object) do + {:ok, + %{ + "id" => Utils.generate_activity_id(), + "actor" => actor.ap_id, + "type" => "Undo", + "object" => object.data["id"], + "to" => object.data["to"] || [], + "cc" => object.data["cc"] || [] + }, []} + end + @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()} def like(actor, object) do object_actor = User.get_cached_by_ap_id(object.data["actor"]) diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index dc4bce059..b6937d2e1 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -12,10 +12,19 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) + def validate(%{"type" => "Undo"} = object, meta) do + with {:ok, object} <- + object |> UndoValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object |> Map.from_struct()) + {:ok, object, meta} + end + end + def validate(%{"type" => "Like"} = object, meta) do with {:ok, object} <- object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do 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 b479c3918..067ee4f9a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do import Ecto.Changeset + alias Pleroma.Activity alias Pleroma.Object alias Pleroma.User @@ -22,7 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do def validate_object_presence(cng, field_name \\ :object) do cng |> validate_change(field_name, fn field_name, object -> - if Object.get_cached_by_ap_id(object) do + if Object.get_cached_by_ap_id(object) || Activity.get_by_ap_id(object) do [] else [{field_name, "can't find object"}] diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex new file mode 100644 index 000000000..d0ba418e8 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex @@ -0,0 +1,62 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do + use Ecto.Schema + + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.ObjectValidators.Types + + import Ecto.Changeset + import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + + @primary_key false + + embedded_schema do + field(:id, Types.ObjectID, primary_key: true) + field(:type, :string) + field(:object, Types.ObjectID) + field(:actor, Types.ObjectID) + field(:to, {:array, :string}, default: []) + field(:cc, {:array, :string}, default: []) + end + + def cast_and_validate(data) do + data + |> cast_data() + |> validate_data() + end + + def cast_data(data) do + %__MODULE__{} + |> changeset(data) + end + + def changeset(struct, data) do + struct + |> cast(data, __schema__(:fields)) + end + + def validate_data(data_cng) do + data_cng + |> validate_inclusion(:type, ["Undo"]) + |> validate_required([:id, :type, :object, :actor, :to, :cc]) + |> validate_actor_presence() + |> validate_object_presence() + |> validate_undo_rights() + end + + def validate_undo_rights(cng) do + actor = get_field(cng, :actor) + object = get_field(cng, :object) + + with %Activity{data: %{"actor" => object_actor}} <- Activity.get_by_ap_id(object), + true <- object_actor != actor do + cng + |> add_error(:actor, "not the same as object actor") + else + _ -> cng + end + end +end -- cgit v1.2.3 From a3071f023166cb5364ce56e3666d5a77baa16434 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 15:08:41 +0200 Subject: Undoing: Move undoing likes to the pipeline everywhere. --- lib/pleroma/user.ex | 12 +++++++---- lib/pleroma/web/activity_pub/activity_pub.ex | 23 ---------------------- lib/pleroma/web/activity_pub/side_effects.ex | 19 ++++++++++++++++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 11 ++--------- lib/pleroma/web/common_api/common_api.ex | 9 ++++++--- .../mastodon_api/controllers/status_controller.ex | 6 +++--- 6 files changed, 38 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 99358ddaf..0136ba119 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -29,7 +29,9 @@ defmodule Pleroma.User do alias Pleroma.UserRelationship alias Pleroma.Web alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidators.Types + alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils @@ -1553,11 +1555,13 @@ defmodule Pleroma.User do end defp delete_activity(%{data: %{"type" => "Like"}} = activity) do - object = Object.normalize(activity) + actor = + activity.actor + |> get_cached_by_ap_id() - activity.actor - |> get_cached_by_ap_id() - |> ActivityPub.unlike(object) + {:ok, undo, _} = Builder.undo(actor, activity) + + Pipeline.common_pipeline(undo, local: true) end defp delete_activity(%{data: %{"type" => "Announce"}} = activity) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1c21d78af..daad4d751 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -398,29 +398,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) :: - {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()} - def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do - with {:ok, result} <- - Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do - result - end - end - - defp do_unlike(actor, object, activity_id, local) do - with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object), - unlike_data <- make_unlike_data(actor, like_activity, activity_id), - {:ok, unlike_activity} <- insert(unlike_data, local), - {:ok, _activity} <- Repo.delete(like_activity), - {:ok, object} <- remove_like_from_object(like_activity, object), - :ok <- maybe_federate(unlike_activity) do - {:ok, unlike_activity, like_activity, object} - else - nil -> {:ok, object} - {:error, error} -> Repo.rollback(error) - end - end - @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) :: {:ok, Activity.t(), Object.t()} | {:error, any()} def announce( diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 6a8f1af96..8ed91e257 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -5,8 +5,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do liked object, a `Follow` activity will add the user to the follower collection, and so on. """ + alias Pleroma.Activity alias Pleroma.Notification alias Pleroma.Object + alias Pleroma.Repo alias Pleroma.Web.ActivityPub.Utils def handle(object, meta \\ []) @@ -23,8 +25,25 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end + def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do + with undone_object <- Activity.get_by_ap_id(undone_object), + :ok <- handle_undoing(undone_object) do + {:ok, object, meta} + end + end + # Nothing to do def handle(object, meta) do {:ok, object, meta} end + + def handle_undoing(%{data: %{"type" => "Like"}} = object) do + with %Object{} = liked_object <- Object.get_by_ap_id(object.data["object"]), + {:ok, _} <- Utils.remove_like_from_object(object, liked_object), + {:ok, _} <- Repo.delete(object) do + :ok + end + end + + def handle_undoing(object), do: {:error, ["don't know how to handle", object]} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 581e7040b..a60b27bea 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -865,19 +865,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming( %{ "type" => "Undo", - "object" => %{"type" => "Like", "object" => object_id}, - "actor" => _actor, - "id" => id + "object" => %{"type" => "Like"} } = data, _options ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- get_obj_helper(object_id), - {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do + with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} - else - _e -> :error end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index f9db97d24..a670ea5bc 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -166,9 +166,12 @@ defmodule Pleroma.Web.CommonAPI do def unfavorite(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- - {:find_activity, Activity.get_by_id(id)} do - object = Object.normalize(activity) - ActivityPub.unlike(user, object) + {:find_activity, Activity.get_by_id(id)}, + %Object{} = note <- Object.normalize(activity, false), + %Activity{} = like <- Utils.get_existing_like(user.ap_id, note), + {:ok, undo, _} <- Builder.undo(user, like), + {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: false) do + {:ok, activity} else {:find_activity, _} -> {:error, :not_found} _ -> {:error, dgettext("errors", "Could not unfavorite")} diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 9eea2e9eb..2a5eac9d9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -222,9 +222,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unfavourite" - def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do - with {:ok, _, _, %{data: %{"id" => id}}} <- CommonAPI.unfavorite(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do + def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + with {:ok, _unfav} <- CommonAPI.unfavorite(activity_id, user), + %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) end end -- cgit v1.2.3 From b34debe61540cf845ccf4ac93066e45a1d9c8f85 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 16:17:09 +0200 Subject: Undoing: Move undoing reactions to the pipeline everywhere. --- lib/pleroma/web/activity_pub/activity_pub.ex | 25 -------------------- lib/pleroma/web/activity_pub/side_effects.ex | 8 +++++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 27 +++------------------- lib/pleroma/web/common_api/common_api.ex | 8 ++++--- .../controllers/pleroma_api_controller.ex | 3 ++- 5 files changed, 18 insertions(+), 53 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index daad4d751..c94af3b5f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -373,31 +373,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - @spec unreact_with_emoji(User.t(), String.t(), keyword()) :: - {:ok, Activity.t(), Object.t()} | {:error, any()} - def unreact_with_emoji(user, reaction_id, options \\ []) do - with {:ok, result} <- - Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do - result - end - end - - defp do_unreact_with_emoji(user, reaction_id, options) do - with local <- Keyword.get(options, :local, true), - activity_id <- Keyword.get(options, :activity_id, nil), - user_ap_id <- user.ap_id, - %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id), - object <- Object.normalize(reaction_activity), - unreact_data <- make_undo_data(user, reaction_activity, activity_id), - {:ok, activity} <- insert(unreact_data, local), - {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object), - :ok <- maybe_federate(activity) do - {:ok, activity, object} - else - {:error, error} -> Repo.rollback(error) - end - end - @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) :: {:ok, Activity.t(), Object.t()} | {:error, any()} def announce( diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 8ed91e257..d58df9394 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -45,5 +45,13 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end + def handle_undoing(%{data: %{"type" => "EmojiReact"}} = object) do + with %Object{} = reacted_object <- Object.get_by_ap_id(object.data["object"]), + {:ok, _} <- Utils.remove_emoji_reaction_from_object(object, reacted_object), + {:ok, _} <- Repo.delete(object) do + :ok + end + end + def handle_undoing(object), do: {:error, ["don't know how to handle", object]} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a60b27bea..94849b5f5 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -806,28 +806,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - def handle_incoming( - %{ - "type" => "Undo", - "object" => %{"type" => "EmojiReact", "id" => reaction_activity_id}, - "actor" => _actor, - "id" => id - } = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, activity, _} <- - ActivityPub.unreact_with_emoji(actor, reaction_activity_id, - activity_id: id, - local: false - ) do - {:ok, activity} - else - _e -> :error - end - end - def handle_incoming( %{ "type" => "Undo", @@ -865,10 +843,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming( %{ "type" => "Undo", - "object" => %{"type" => "Like"} + "object" => %{"type" => type} } = data, _options - ) do + ) + when type in ["Like", "EmojiReact"] do with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index a670ea5bc..067ac875e 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -170,7 +170,7 @@ defmodule Pleroma.Web.CommonAPI do %Object{} = note <- Object.normalize(activity, false), %Activity{} = like <- Utils.get_existing_like(user.ap_id, note), {:ok, undo, _} <- Builder.undo(user, like), - {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: false) do + {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do {:ok, activity} else {:find_activity, _} -> {:error, :not_found} @@ -189,8 +189,10 @@ defmodule Pleroma.Web.CommonAPI do end def unreact_with_emoji(id, user, emoji) do - with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji) do - ActivityPub.unreact_with_emoji(user, reaction_activity.data["id"]) + with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji), + {:ok, undo, _} <- Builder.undo(user, reaction_activity), + {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do + {:ok, activity} else _ -> {:error, dgettext("errors", "Could not remove reaction emoji")} diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 1bdb3aa4d..4aa5c1dd8 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -98,7 +98,8 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do "id" => activity_id, "emoji" => emoji }) do - with {:ok, _activity, _object} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji), + with {:ok, _activity} <- + CommonAPI.unreact_with_emoji(activity_id, user, emoji), activity <- Activity.get_by_id(activity_id) do conn |> put_view(StatusView) -- cgit v1.2.3 From a3bb2e5474ee068bf375b24df8906e51654c9699 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 16:42:34 +0200 Subject: Undoing: Move undoing announcements to the pipeline everywhere. --- lib/pleroma/user.ex | 10 +------- lib/pleroma/web/activity_pub/activity_pub.ex | 28 ---------------------- lib/pleroma/web/activity_pub/side_effects.ex | 8 +++++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 21 +--------------- lib/pleroma/web/common_api/common_api.ex | 9 ++++--- .../mastodon_api/controllers/status_controller.ex | 6 ++--- 6 files changed, 19 insertions(+), 63 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0136ba119..aa675a521 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1554,7 +1554,7 @@ defmodule Pleroma.User do |> ActivityPub.delete() end - defp delete_activity(%{data: %{"type" => "Like"}} = activity) do + defp delete_activity(%{data: %{"type" => type}} = activity) when type in ["Like", "Announce"] do actor = activity.actor |> get_cached_by_ap_id() @@ -1564,14 +1564,6 @@ defmodule Pleroma.User do Pipeline.common_pipeline(undo, local: true) end - defp delete_activity(%{data: %{"type" => "Announce"}} = activity) do - object = Object.normalize(activity) - - activity.actor - |> get_cached_by_ap_id() - |> ActivityPub.unannounce(object) - end - defp delete_activity(_activity), do: "Doing nothing" def html_filter_policy(%User{no_rich_text: true}) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index c94af3b5f..be3d72c82 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -402,34 +402,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - @spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) :: - {:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()} - def unannounce( - %User{} = actor, - %Object{} = object, - activity_id \\ nil, - local \\ true - ) do - with {:ok, result} <- - Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do - result - end - end - - defp do_unannounce(actor, object, activity_id, local) do - with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object), - unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id), - {:ok, unannounce_activity} <- insert(unannounce_data, local), - :ok <- maybe_federate(unannounce_activity), - {:ok, _activity} <- Repo.delete(announce_activity), - {:ok, object} <- remove_announce_from_object(announce_activity, object) do - {:ok, unannounce_activity, object} - else - nil -> {:ok, object} - {:error, error} -> Repo.rollback(error) - end - end - @spec follow(User.t(), User.t(), String.t() | nil, boolean()) :: {:ok, Activity.t()} | {:error, any()} def follow(follower, followed, activity_id \\ nil, local \\ true) do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index d58df9394..146d30ac1 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -53,5 +53,13 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end + def handle_undoing(%{data: %{"type" => "Announce"}} = object) do + with %Object{} = liked_object <- Object.get_by_ap_id(object.data["object"]), + {:ok, _} <- Utils.remove_announce_from_object(object, liked_object), + {:ok, _} <- Repo.delete(object) do + :ok + end + end + def handle_undoing(object), do: {:error, ["don't know how to handle", object]} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 94849b5f5..afa171448 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -768,25 +768,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - def handle_incoming( - %{ - "type" => "Undo", - "object" => %{"type" => "Announce", "object" => object_id}, - "actor" => _actor, - "id" => id - } = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- get_obj_helper(object_id), - {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do - {:ok, activity} - else - _e -> :error - end - end - def handle_incoming( %{ "type" => "Undo", @@ -847,7 +828,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do } = data, _options ) - when type in ["Like", "EmojiReact"] do + when type in ["Like", "EmojiReact", "Announce"] do with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 067ac875e..fc8246871 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -107,9 +107,12 @@ defmodule Pleroma.Web.CommonAPI do def unrepeat(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- - {:find_activity, Activity.get_by_id(id)} do - object = Object.normalize(activity) - ActivityPub.unannounce(user, object) + {:find_activity, Activity.get_by_id(id)}, + %Object{} = note <- Object.normalize(activity, false), + %Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note), + {:ok, undo, _} <- Builder.undo(user, announce), + {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do + {:ok, activity} else {:find_activity, _} -> {:error, :not_found} _ -> {:error, dgettext("errors", "Could not unrepeat")} diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 2a5eac9d9..12e3ba15e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -206,9 +206,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do end @doc "POST /api/v1/statuses/:id/unreblog" - def unreblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do - with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), - %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do + def unreblog(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + with {:ok, _unannounce} <- CommonAPI.unrepeat(activity_id, user), + %Activity{} = activity <- Activity.get_by_id(activity_id) do try_render(conn, "show.json", %{activity: activity, for: user, as: :activity}) end end -- cgit v1.2.3 From 92caae592338a3ca307686e7644f2de18bb57ce5 Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 5 May 2020 18:00:37 +0200 Subject: Undoing: Move undoing blocks to the pipeline everywhere. --- lib/pleroma/web/activity_pub/activity_pub.ex | 21 --------- lib/pleroma/web/activity_pub/side_effects.ex | 12 +++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 51 +++++++--------------- lib/pleroma/web/activity_pub/utils.ex | 49 --------------------- lib/pleroma/web/common_api/common_api.ex | 8 ++++ .../mastodon_api/controllers/account_controller.ex | 3 +- 6 files changed, 37 insertions(+), 107 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index be3d72c82..78e8c0cbe 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -532,27 +532,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - @spec unblock(User.t(), User.t(), String.t() | nil, boolean()) :: - {:ok, Activity.t()} | {:error, any()} | nil - def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do - with {:ok, result} <- - Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do - result - end - end - - defp do_unblock(blocker, blocked, activity_id, local) do - with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked), - unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id), - {:ok, activity} <- insert(unblock_data, local), - :ok <- maybe_federate(activity) do - {:ok, activity} - else - nil -> nil - {:error, error} -> Repo.rollback(error) - end - end - @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()} def flag( %{ diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 146d30ac1..3fad6e4d8 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.ActivityPub.Utils def handle(object, meta \\ []) @@ -61,5 +62,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end + def handle_undoing( + %{data: %{"type" => "Block", "actor" => blocker, "object" => blocked}} = object + ) do + with %User{} = blocker <- User.get_cached_by_ap_id(blocker), + %User{} = blocked <- User.get_cached_by_ap_id(blocked), + {:ok, _} <- User.unblock(blocker, blocked), + {:ok, _} <- Repo.delete(object) do + :ok + end + end + def handle_undoing(object), do: {:error, ["don't know how to handle", object]} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index afa171448..65ae643ed 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -787,40 +787,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - def handle_incoming( - %{ - "type" => "Undo", - "object" => %{"type" => "Block", "object" => blocked}, - "actor" => blocker, - "id" => id - } = _data, - _options - ) do - with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), - {:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker), - {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do - User.unblock(blocker, blocked) - {:ok, activity} - else - _e -> :error - end - end - - def handle_incoming( - %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data, - _options - ) do - with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), - {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker), - {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do - User.unfollow(blocker, blocked) - User.block(blocker, blocked) - {:ok, activity} - else - _e -> :error - end - end - def handle_incoming( %{ "type" => "Undo", @@ -828,7 +794,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do } = data, _options ) - when type in ["Like", "EmojiReact", "Announce"] do + when type in ["Like", "EmojiReact", "Announce", "Block"] do with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} end @@ -852,6 +818,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end + def handle_incoming( + %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data, + _options + ) do + with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), + {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker), + {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do + User.unfollow(blocker, blocked) + User.block(blocker, blocked) + {:ok, activity} + else + _e -> :error + end + end + def handle_incoming( %{ "type" => "Move", diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 2d685ecc0..95fb382f0 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -562,45 +562,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> maybe_put("id", activity_id) end - @doc """ - Make unannounce activity data for the given actor and object - """ - def make_unannounce_data( - %User{ap_id: ap_id} = user, - %Activity{data: %{"context" => context, "object" => object}} = activity, - activity_id - ) do - object = Object.normalize(object) - - %{ - "type" => "Undo", - "actor" => ap_id, - "object" => activity.data, - "to" => [user.follower_address, object.data["actor"]], - "cc" => [Pleroma.Constants.as_public()], - "context" => context - } - |> maybe_put("id", activity_id) - end - - def make_unlike_data( - %User{ap_id: ap_id} = user, - %Activity{data: %{"context" => context, "object" => object}} = activity, - activity_id - ) do - object = Object.normalize(object) - - %{ - "type" => "Undo", - "actor" => ap_id, - "object" => activity.data, - "to" => [user.follower_address, object.data["actor"]], - "cc" => [Pleroma.Constants.as_public()], - "context" => context - } - |> maybe_put("id", activity_id) - end - def make_undo_data( %User{ap_id: actor, follower_address: follower_address}, %Activity{ @@ -688,16 +649,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> maybe_put("id", activity_id) end - def make_unblock_data(blocker, blocked, block_activity, activity_id) do - %{ - "type" => "Undo", - "actor" => blocker.ap_id, - "to" => [blocked.ap_id], - "object" => block_activity.data - } - |> maybe_put("id", activity_id) - end - #### Create-related helpers def make_create_data(params, additional) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index fc8246871..2a1eb7f37 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -24,6 +24,14 @@ defmodule Pleroma.Web.CommonAPI do require Pleroma.Constants require Logger + def unblock(blocker, blocked) do + with %Activity{} = block <- Utils.fetch_latest_block(blocker, blocked), + {:ok, unblock_data, _} <- Builder.undo(blocker, block), + {:ok, unblock, _} <- Pipeline.common_pipeline(unblock_data, local: true) do + {:ok, unblock} + end + end + def follow(follower, followed) do timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout]) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 61b0e2f63..2b208ddab 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -356,8 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/unblock" def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, _user_block} <- User.unblock(blocker, blocked), - {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do + with {:ok, _activity} <- CommonAPI.unblock(blocker, blocked) do render(conn, "relationship.json", user: blocker, target: blocked) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) -- cgit v1.2.3 From bd261309cc27ebf5d2f78ea3c1474fe71ae8046d Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 6 May 2020 15:08:38 +0300 Subject: added `unread_notifications_count` for `/api/v1/accounts/verify_credentials` --- lib/pleroma/notification.ex | 8 ++++++ lib/pleroma/web/mastodon_api/views/account_view.ex | 29 +++++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 7fd1b2ff6..c135306ca 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -36,6 +36,14 @@ defmodule Pleroma.Notification do timestamps() end + @spec unread_notifications_count(User.t()) :: integer() + def unread_notifications_count(%User{id: user_id}) do + from(q in __MODULE__, + where: q.user_id == ^user_id and q.seen == false + ) + |> Repo.aggregate(:count, :id) + end + def changeset(%Notification{} = notification, attrs) do notification |> cast(attrs, [:seen]) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b4b61e74c..420bd586f 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -36,9 +36,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do end def render("show.json", %{user: user} = opts) do - if User.visible_for?(user, opts[:for]), - do: do_render("show.json", opts), - else: %{} + if User.visible_for?(user, opts[:for]) do + do_render("show.json", opts) + else + %{} + end end def render("mention.json", %{user: user}) do @@ -221,7 +223,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do fields: user.fields, bot: bot, source: %{ - note: (user.bio || "") |> String.replace(~r(
), "\n") |> Pleroma.HTML.strip_tags(), + note: prepare_user_bio(user), sensitive: false, fields: user.raw_fields, pleroma: %{ @@ -253,8 +255,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> maybe_put_follow_requests_count(user, opts[:for]) |> maybe_put_allow_following_move(user, opts[:for]) |> maybe_put_unread_conversation_count(user, opts[:for]) + |> maybe_put_unread_notification_count(user, opts[:for]) end + defp prepare_user_bio(%User{bio: ""}), do: "" + + defp prepare_user_bio(%User{bio: bio}) when is_binary(bio) do + bio |> String.replace(~r(
), "\n") |> Pleroma.HTML.strip_tags() + end + + defp prepare_user_bio(_), do: "" + defp username_from_nickname(string) when is_binary(string) do hd(String.split(string, "@")) end @@ -350,6 +361,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_unread_conversation_count(data, _, _), do: data + defp maybe_put_unread_notification_count(data, %User{id: user_id}, %User{id: user_id} = user) do + Kernel.put_in( + data, + [:pleroma, :unread_notifications_count], + Pleroma.Notification.unread_notifications_count(user) + ) + end + + defp maybe_put_unread_notification_count(data, _, _), do: data + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end -- cgit v1.2.3 From 57736c18332b0017e01d90e56547af1f5f830b7a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 6 May 2020 16:30:05 -0500 Subject: Privacy option affects all push notifications, not just Direct Messages --- lib/pleroma/web/push/impl.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index a9f893f7b..7f80bb0c9 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -106,14 +106,13 @@ defmodule Pleroma.Web.Push.Impl do def build_content( %{ - activity: %{data: %{"directMessage" => true}}, user: %{notification_settings: %{privacy_option: true}} - }, + } = notification, actor, _object, - _mastodon_type + mastodon_type ) do - %{title: "New Direct Message", body: "@#{actor.nickname}"} + %{title: format_title(notification, mastodon_type), body: "@#{actor.nickname}"} end def build_content(notification, actor, object, mastodon_type) do -- cgit v1.2.3 From a2580adc91ac757e47b88839f5fb723fb15305b1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 6 May 2020 16:42:27 -0500 Subject: Hide the sender when privacy option is enabled --- lib/pleroma/web/push/impl.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 7f80bb0c9..691725702 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -108,11 +108,11 @@ defmodule Pleroma.Web.Push.Impl do %{ user: %{notification_settings: %{privacy_option: true}} } = notification, - actor, + _actor, _object, mastodon_type ) do - %{title: format_title(notification, mastodon_type), body: "@#{actor.nickname}"} + %{body: format_title(notification, mastodon_type)} end def build_content(notification, actor, object, mastodon_type) do -- cgit v1.2.3 From cdca62e8d4772240c513acc08a627d2f0ee0eed4 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 6 May 2020 19:20:26 +0400 Subject: Add schema for Tag --- lib/pleroma/web/api_spec/schemas/status.ex | 12 ++---------- lib/pleroma/web/api_spec/schemas/tag.ex | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 lib/pleroma/web/api_spec/schemas/tag.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 7a804461f..2572c9641 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do alias Pleroma.Web.ApiSpec.Schemas.Emoji alias Pleroma.Web.ApiSpec.Schemas.FlakeID alias Pleroma.Web.ApiSpec.Schemas.Poll + alias Pleroma.Web.ApiSpec.Schemas.Tag alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope require OpenApiSpex @@ -106,16 +107,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do replies_count: %Schema{type: :integer}, sensitive: %Schema{type: :boolean}, spoiler_text: %Schema{type: :string}, - tags: %Schema{ - type: :array, - items: %Schema{ - type: :object, - properties: %{ - name: %Schema{type: :string}, - url: %Schema{type: :string, format: :uri} - } - } - }, + tags: %Schema{type: :array, items: Tag}, uri: %Schema{type: :string, format: :uri}, url: %Schema{type: :string, nullable: true, format: :uri}, visibility: VisibilityScope diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex new file mode 100644 index 000000000..e693fb83e --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/tag.ex @@ -0,0 +1,27 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.Tag do + alias OpenApiSpex.Schema + + require OpenApiSpex + + OpenApiSpex.schema(%{ + title: "Tag", + description: "Represents a hashtag used within the content of a status", + type: :object, + properties: %{ + name: %Schema{type: :string, description: "The value of the hashtag after the # sign"}, + url: %Schema{ + type: :string, + format: :uri, + description: "A link to the hashtag on the instance" + } + }, + example: %{ + name: "cofe", + url: "https://lain.com/tag/cofe" + } + }) +end -- cgit v1.2.3 From dc4a448f4863e7d69c55d39273575fb3463c6c3c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 7 May 2020 14:04:48 +0400 Subject: Add OpenAPI spec for SearchController --- .../web/api_spec/operations/account_operation.ex | 5 +- .../web/api_spec/operations/search_operation.ex | 207 +++++++++++++++++++++ .../mastodon_api/controllers/search_controller.ex | 24 ++- 3 files changed, 224 insertions(+), 12 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/search_operation.ex (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 470fc0215..70069d6f9 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -556,11 +556,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - defp array_of_accounts do + def array_of_accounts do %Schema{ title: "ArrayOfAccounts", type: :array, - items: Account + items: Account, + example: [Account.schema().example] } end diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex new file mode 100644 index 000000000..ec1ae5dcf --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -0,0 +1,207 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.SearchOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.Account + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.Schemas.Status + alias Pleroma.Web.ApiSpec.Schemas.Tag + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def account_search_operation do + %Operation{ + tags: ["Search"], + summary: "Search for matching accounts by username or display name", + operationId: "SearchController.account_search", + parameters: [ + Operation.parameter(:q, :query, %Schema{type: :string}, "What to search for", + required: true + ), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 40}, + "Maximum number of results" + ), + Operation.parameter( + :resolve, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Attempt WebFinger lookup. Use this when `q` is an exact address." + ), + Operation.parameter( + :following, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Only who the user is following." + ) + ], + responses: %{ + 200 => + Operation.response( + "Array of Account", + "application/json", + AccountOperation.array_of_accounts() + ) + } + } + end + + def search_operation do + %Operation{ + tags: ["Search"], + summary: "Search results", + security: [%{"oAuth" => ["read:search"]}], + operationId: "SearchController.search", + deprecated: true, + parameters: [ + Operation.parameter( + :account_id, + :query, + FlakeID, + "If provided, statuses returned will be authored only by this account" + ), + Operation.parameter( + :type, + :query, + %Schema{type: :string, enum: ["accounts", "hashtags", "statuses"]}, + "Search type" + ), + Operation.parameter(:q, :query, %Schema{type: :string}, "The search query", required: true), + Operation.parameter( + :resolve, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Attempt WebFinger lookup" + ), + Operation.parameter( + :following, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Only include accounts that the user is following" + ), + Operation.parameter( + :offset, + :query, + %Schema{type: :integer}, + "Offset" + ) + | pagination_params() + ], + responses: %{ + 200 => Operation.response("Results", "application/json", results()) + } + } + end + + def search2_operation do + %Operation{ + tags: ["Search"], + summary: "Search results", + security: [%{"oAuth" => ["read:search"]}], + operationId: "SearchController.search2", + parameters: [ + Operation.parameter( + :account_id, + :query, + FlakeID, + "If provided, statuses returned will be authored only by this account" + ), + Operation.parameter( + :type, + :query, + %Schema{type: :string, enum: ["accounts", "hashtags", "statuses"]}, + "Search type" + ), + Operation.parameter(:q, :query, %Schema{type: :string}, "What to search for", + required: true + ), + Operation.parameter( + :resolve, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Attempt WebFinger lookup" + ), + Operation.parameter( + :following, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Only include accounts that the user is following" + ) + | pagination_params() + ], + responses: %{ + 200 => Operation.response("Results", "application/json", results2()) + } + } + end + + defp results2 do + %Schema{ + title: "SearchResults", + type: :object, + properties: %{ + accounts: %Schema{ + type: :array, + items: Account, + description: "Accounts which match the given query" + }, + statuses: %Schema{ + type: :array, + items: Status, + description: "Statuses which match the given query" + }, + hashtags: %Schema{ + type: :array, + items: Tag, + description: "Hashtags which match the given query" + } + }, + example: %{ + "accounts" => [Account.schema().example], + "statuses" => [Status.schema().example], + "hashtags" => [Tag.schema().example] + } + } + end + + defp results do + %Schema{ + title: "SearchResults", + type: :object, + properties: %{ + accounts: %Schema{ + type: :array, + items: Account, + description: "Accounts which match the given query" + }, + statuses: %Schema{ + type: :array, + items: Status, + description: "Statuses which match the given query" + }, + hashtags: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: "Hashtags which match the given query" + } + }, + example: %{ + "accounts" => [Account.schema().example], + "statuses" => [Status.schema().example], + "hashtags" => ["cofe"] + } + } + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index cd49da6ad..0e0d54ba4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 2, skip_relationships?: 1] + import Pleroma.Web.ControllerHelper, only: [skip_relationships?: 1] alias Pleroma.Activity alias Pleroma.Plugs.OAuthScopesPlug @@ -18,6 +18,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do require Logger + plug(Pleroma.Web.ApiSpec.CastAndValidate) + # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) @@ -25,7 +27,9 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search]) - def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.SearchOperation + + def account_search(%{assigns: %{user: user}} = conn, %{q: query} = params) do accounts = User.search(query, search_options(params, user)) conn @@ -36,7 +40,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do def search2(conn, params), do: do_search(:v2, conn, params) def search(conn, params), do: do_search(:v1, conn, params) - defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = params) do + defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do options = search_options(params, user) timeout = Keyword.get(Repo.config(), :timeout, 15_000) default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []} @@ -44,7 +48,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do result = default_values |> Enum.map(fn {resource, default_value} -> - if params["type"] in [nil, resource] do + if params[:type] in [nil, resource] do {resource, fn -> resource_search(version, resource, query, options) end} else {resource, fn -> default_value end} @@ -68,11 +72,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do defp search_options(params, user) do [ skip_relationships: skip_relationships?(params), - resolve: params["resolve"] == "true", - following: params["following"] == "true", - limit: fetch_integer_param(params, "limit"), - offset: fetch_integer_param(params, "offset"), - type: params["type"], + resolve: params[:resolve], + following: params[:following], + limit: params[:limit], + offset: params[:offset], + type: params[:type], author: get_author(params), for_user: user ] @@ -135,7 +139,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do end end - defp get_author(%{"account_id" => account_id}) when is_binary(account_id), + defp get_author(%{account_id: account_id}) when is_binary(account_id), do: User.get_cached_by_id(account_id) defp get_author(_params), do: nil -- cgit v1.2.3 From 3f867d8e9bf970e180153b411d5924f15c490046 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 7 May 2020 10:48:09 +0000 Subject: Apply suggestion to lib/pleroma/web/api_spec/operations/search_operation.ex --- lib/pleroma/web/api_spec/operations/search_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex index ec1ae5dcf..6ea00a9a8 100644 --- a/lib/pleroma/web/api_spec/operations/search_operation.ex +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -44,7 +44,7 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do :following, :query, %Schema{allOf: [BooleanLike], default: false}, - "Only who the user is following." + "Only include accounts that the user is following" ) ], responses: %{ -- cgit v1.2.3 From 788b7e7bbd2732e2af72adad1a660cf363486c6b Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 7 May 2020 14:52:37 +0200 Subject: Merge fixes. --- lib/pleroma/user.ex | 13 +++++-------- lib/pleroma/web/activity_pub/object_validator.ex | 6 ++++-- .../activity_pub/object_validators/common_validations.ex | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 921bdd93a..2a6a23fec 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1557,16 +1557,13 @@ defmodule Pleroma.User do defp delete_activity(%{data: %{"type" => "Create", "object" => object}}, user) do {:ok, delete_data, _} = Builder.delete(user, object) - Pipeline.common_pipeline(delete_data, local: true) + Pipeline.common_pipeline(delete_data, local: user.local) end - defp delete_activity(%{data: %{"type" => type}} = activity) when type in ["Like", "Announce"] do - actor = - activity.actor - |> get_cached_by_ap_id() - - {:ok, undo, _} = Builder.undo(actor, activity) - Pipeline.common_pipeline(undo, local: true) + defp delete_activity(%{data: %{"type" => type}} = activity, user) + when type in ["Like", "Announce"] do + {:ok, undo, _} = Builder.undo(user, activity) + Pipeline.common_pipeline(undo, local: user.local) end defp delete_activity(_activity, _user), do: "Doing nothing" diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 8e043287d..1f0431b36 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -21,8 +21,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def validate(%{"type" => "Undo"} = object, meta) do with {:ok, object} <- - object |> UndoValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object |> Map.from_struct()) + object + |> UndoValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) {:ok, object, meta} end end 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 2ada9f09e..aeef31945 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -48,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do cng |> validate_change(field_name, fn field_name, object_id -> - object = Object.get_cached_by_ap_id(object_id) || Activity.get_by_ap_id(object) + object = Object.get_cached_by_ap_id(object_id) || Activity.get_by_ap_id(object_id) cond do !object -> -- cgit v1.2.3 From d11eea62b139ce16d7dffbd574947b2550df238f Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 7 May 2020 15:09:37 +0200 Subject: Credo fixes --- lib/pleroma/web/activity_pub/object_validator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 1f0431b36..4782cd8f3 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -13,8 +13,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator alias Pleroma.Web.ActivityPub.ObjectValidators.Types + alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) -- cgit v1.2.3 From eb1f2fcbc62735a6e1a24c7c5591061d9391e808 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 7 May 2020 16:13:24 +0300 Subject: Streamer: Fix wrong argument order when rendering activities to authenticated user Closes #1747 --- lib/pleroma/web/mastodon_api/websocket_handler.ex | 2 +- lib/pleroma/web/views/streamer_view.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 6ef3fe2dd..e2ffd02d0 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -78,7 +78,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do user = %User{} = User.get_cached_by_ap_id(state.user.ap_id) unless Streamer.filtered_by_user?(user, item) do - websocket_info({:text, view.render(template, user, item)}, %{state | user: user}) + websocket_info({:text, view.render(template, item, user)}, %{state | user: user}) else {:ok, state} end diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex index 443868878..237b29ded 100644 --- a/lib/pleroma/web/views/streamer_view.ex +++ b/lib/pleroma/web/views/streamer_view.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Web.StreamerView do |> Jason.encode!() end - def render("notification.json", %User{} = user, %Notification{} = notify) do + def render("notification.json", %Notification{} = notify, %User{} = user) do %{ event: "notification", payload: -- cgit v1.2.3 From 6acbe45eb211286e747143f6bd6edaa5c2126657 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 8 May 2020 11:30:31 +0200 Subject: Builder: Extract common features of likes and reactions. --- lib/pleroma/web/activity_pub/builder.ex | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index f6544d3f5..922a444a9 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()} def emoji_react(actor, object, emoji) do - with {:ok, data, meta} <- like(actor, object) do + with {:ok, data, meta} <- object_action(actor, object) do data = data |> Map.put("content", emoji) @@ -64,6 +64,17 @@ defmodule Pleroma.Web.ActivityPub.Builder do @spec like(User.t(), Object.t()) :: {:ok, map(), keyword()} def like(actor, object) do + with {:ok, data, meta} <- object_action(actor, object) do + data = + data + |> Map.put("type", "Like") + + {:ok, data, meta} + end + end + + @spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()} + defp object_action(actor, object) do object_actor = User.get_cached_by_ap_id(object.data["actor"]) # Address the actor of the object, and our actor's follower collection if the post is public. @@ -85,7 +96,6 @@ defmodule Pleroma.Web.ActivityPub.Builder do %{ "id" => Utils.generate_activity_id(), "actor" => actor.ap_id, - "type" => "Like", "object" => object.data["id"], "to" => to, "cc" => cc, -- cgit v1.2.3