diff options
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 12 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/utils.ex | 45 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 10 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 7 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 18 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 4 | ||||
| -rw-r--r-- | test/web/activity_pub/activity_pub_test.exs | 31 | ||||
| -rw-r--r-- | test/web/mastodon_api/mastodon_api_controller_test.exs | 19 | 
9 files changed, 145 insertions, 2 deletions
| diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 04b50c1cc..dccd2e757 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -140,6 +140,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      end    end +  def unannounce(%User{} = actor, %Object{} = object, local \\ true) do +    with %Activity{} = activity <- get_existing_announce(actor.ap_id, object), +         unannounce_data <- make_unannounce_data(actor, object), +         {:ok, unannounce_activity} <- insert(unannounce_data, local), +         {:ok, _activity} <- Repo.delete(activity), +         {:ok, object} <- remove_announce_from_object(activity, object) do +      {:ok, unannounce_activity, activity, object} +    else +      _e -> {:ok, object} +    end +  end +    def follow(follower, followed, activity_id \\ nil, local \\ true) do      with data <- make_follow_data(follower, followed, activity_id),           {:ok, activity} <- insert(data, local), diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 7a0762e9f..1f740eda5 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -237,6 +237,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do    #### Announce-related helpers    @doc """ +  Retruns an existing announce activity if the notice has already been announced +  """ +  def get_existing_announce(actor, %{data: %{"id" => id}}) do +    query = +      from( +        activity in Activity, +        where: fragment("(?)->>'actor' = ?", activity.data, ^actor), +        # this is to use the index +        where: +          fragment( +            "coalesce((?)->'object'->>'id', (?)->>'object') = ?", +            activity.data, +            activity.data, +            ^id +          ), +        where: fragment("(?)->>'type' = 'Announce'", activity.data) +      ) + +    Repo.one(query) +  end + +  @doc """    Make announce activity data for the given actor and object    """    def make_announce_data( @@ -256,12 +278,35 @@ defmodule Pleroma.Web.ActivityPub.Utils do      if activity_id, do: Map.put(data, "id", activity_id), else: data    end +  @doc """ +  Make unannounce activity data for the given actor and object +  """ +  def make_unannounce_data( +        %User{ap_id: ap_id} = user, +        %Object{data: %{"id" => id, "context" => context}} = object +      ) do +    %{ +      "type" => "Undo", +      "actor" => ap_id, +      "object" => id, +      "to" => [user.follower_address, object.data["actor"]], +      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "context" => context +    } +  end +    def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do      with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do        update_element_in_object("announcement", announcements, object)      end    end +  def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do +    with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do +      update_element_in_object("announcement", announcements, object) +    end +  end +    #### Unfollow-related helpers    def make_unfollow_data(follower, followed, follow_activity) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 21225c3b7..8889b9b42 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -24,6 +24,16 @@ defmodule Pleroma.Web.CommonAPI do      end    end +  def unrepeat(id_or_ap_id, user) do +    with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), +         object <- Object.get_by_ap_id(activity.data["object"]["id"]) do +      ActivityPub.unannounce(user, object) +    else +      _ -> +        {:error, "Could not unrepeat"} +    end +  end +    def favorite(id_or_ap_id, user) do      with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),           false <- activity.data["actor"] == user.ap_id, diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 8b3492332..a49be0588 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -296,6 +296,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end +  def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do +    with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unrepeat(ap_id_or_id, user), +         %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do +      render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) +    end +  end +    def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do      with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),           %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 8f63fdc70..56dc6533b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -110,6 +110,7 @@ defmodule Pleroma.Web.Router do      delete("/statuses/:id", MastodonAPIController, :delete_status)      post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) +    post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)      post("/statuses/:id/favourite", MastodonAPIController, :fav_status)      post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index c12cd7f8a..b6ae7c7f7 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -12,6 +12,18 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do      CommonAPI.post(user, data)    end +  def delete(%User{} = user, id) do +    # TwitterAPI does not have an "unretweet" endpoint; instead this is done +    # via the "destroy" endpoint.  Therefore, there is a need to handle +    # when the status to "delete" is actually an Announce (repeat) object. +    with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do +      case type do +        "Announce" -> unrepeat(user, id) +        _ -> CommonAPI.delete(id, user) +      end +    end +  end +    def follow(%User{} = follower, params) do      with {:ok, %User{} = followed} <- get_user(params),           {:ok, follower} <- User.follow(follower, followed), @@ -64,6 +76,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do      end    end +  defp unrepeat(%User{} = user, ap_id_or_id) do +    with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do +      {:ok, activity} +    end +  end +    def fav(%User{} = user, ap_id_or_id) do      with {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),           %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 6cf8682b8..429a0b7ac 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -157,8 +157,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    end    def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do -    with {:ok, delete} <- CommonAPI.delete(id, user) do -      render(conn, ActivityView, "activity.json", %{activity: delete, for: user}) +    with {:ok, activity} <- TwitterAPI.delete(user, id) do +      render(conn, ActivityView, "activity.json", %{activity: activity, for: user})      end    end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 657d75a55..6a07da775 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -271,6 +271,37 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end +  describe "unannouncing an object" do +    test "unannouncing a previously announced object" do +      note_activity = insert(:note_activity) +      object = Object.get_by_ap_id(note_activity.data["object"]["id"]) +      user = insert(:user) + +      # Unannouncing an object that is not announced does nothing +      #{:ok, object} = ActivityPub.unannounce(user, object) +      #assert object.data["announcement_count"] == 0 + +      {:ok, announce_activity, object} = ActivityPub.announce(user, object) +      assert object.data["announcement_count"] == 1 + +      {:ok, unannounce_activity, activity, object} = ActivityPub.unannounce(user, object) +      assert object.data["announcement_count"] == 0 + +      assert activity == announce_activity + +      assert unannounce_activity.data["to"] == [ +               User.ap_followers(user), +               note_activity.data["actor"] +      ] +      assert unannounce_activity.data["type"] == "Undo" +      assert unannounce_activity.data["object"] == object.data["id"] +      assert unannounce_activity.data["actor"] == user.ap_id +      assert unannounce_activity.data["context"] == object.data["context"] + +      assert Repo.get(Activity, announce_activity.id) == nil +    end +  end +    describe "uploading files" do      test "copies the file to the configured folder" do        file = %Plug.Upload{ diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 5293b9364..2a24037d7 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -264,6 +264,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end +  describe "unreblogging" do +    test "unreblogs and returns the unreblogged status", %{conn: conn} do +      activity = insert(:note_activity) +      user = insert(:user) + +      {:ok, _, _} = CommonAPI.repeat(activity.id, user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses/#{activity.id}/unreblog") + +      assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} =  +               json_response(conn, 200) + +      assert to_string(activity.id) == id +    end +  end +    describe "favoriting" do      test "favs a status and returns it", %{conn: conn} do        activity = insert(:note_activity) | 
