Add `local` visibility
authorEgor Kislitsyn <egor@kislitsyn.com>
Wed, 11 Nov 2020 14:47:57 +0000 (18:47 +0400)
committerEgor Kislitsyn <egor@kislitsyn.com>
Wed, 11 Nov 2020 14:54:01 +0000 (18:54 +0400)
14 files changed:
docs/API/differences_in_mastoapi_responses.md
lib/pleroma/activity.ex
lib/pleroma/web/activity_pub/builder.ex
lib/pleroma/web/activity_pub/pipeline.ex
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/activity_pub/visibility.ex
lib/pleroma/web/api_spec/operations/status_operation.ex
lib/pleroma/web/api_spec/schemas/visibility_scope.ex
lib/pleroma/web/common_api.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
test/pleroma/web/common_api_test.exs
test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
test/pleroma/web/mastodon_api/views/status_view_test.exs

index 1e932d908bb4be89e72291602f485f4c8e74c039..c6d822bfc508499a03822a73d11c605025c6221b 100644 (file)
@@ -14,7 +14,7 @@ Adding the parameter `reply_visibility` to the public and home timelines queries
 
 ## Statuses
 
-- `visibility`: has an additional possible value `list`
+- `visibility`: has additional possible values `list` and `local` (for local-only statuses)
 
 Has these additional fields under the `pleroma` object:
 
@@ -28,7 +28,6 @@ Has these additional fields under the `pleroma` object:
 - `thread_muted`: true if the thread the post belongs to is muted
 - `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
 - `parent_visible`: If the parent of this post is visible to the user or not.
-- `local_only`: true for local-only, non-federated posts.
 
 ## Media Attachments
 
@@ -152,10 +151,9 @@ Additional parameters can be added to the JSON body/Form data:
 - `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
 - `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
 - `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.
-- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
+- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
 - `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
 - `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
-- `local_only`: boolean, if set to `true` the post won't be federated.
 
 ## GET `/api/v1/statuses`
 
index 648cfb623a0291a092d813ddc20fb60b17d5c49f..553834da0b786f905f1544fc9feb395c66bc0285 100644 (file)
@@ -19,8 +19,6 @@ defmodule Pleroma.Activity do
   import Ecto.Changeset
   import Ecto.Query
 
-  require Pleroma.Constants
-
   @type t :: %__MODULE__{}
   @type actor :: String.t()
 
@@ -358,12 +356,4 @@ defmodule Pleroma.Activity do
     actor = user_actor(activity)
     activity.id in actor.pinned_activities
   end
-
-  def local_only?(activity) do
-    recipients = Enum.concat(activity.data["to"], Map.get(activity.data, "cc", []))
-    public = Pleroma.Constants.as_public()
-    local = Pleroma.Constants.as_local_public()
-
-    Enum.member?(recipients, local) and not Enum.member?(recipients, public)
-  end
 end
index c9200a3f0c880603eef78766d37472ef7b53a909..e99f6fd83c02031435b91849b88afae3af7fd5df 100644 (file)
@@ -222,7 +222,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
         actor.ap_id == Relay.ap_id() ->
           [actor.follower_address]
 
-        public? and Pleroma.Activity.local_only?(object) ->
+        public? and Visibility.is_local_public?(object) ->
           [actor.follower_address, object.data["actor"], Pleroma.Constants.as_local_public()]
 
         public? ->
index 559c8387e6671938992a88927b21a0db2ca2b8fe..98c32a42b87bd6545263fb1dfd24f0ca99e8ca8d 100644 (file)
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
   alias Pleroma.Web.ActivityPub.MRF
   alias Pleroma.Web.ActivityPub.ObjectValidator
   alias Pleroma.Web.ActivityPub.SideEffects
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.Federator
 
   @spec common_pipeline(map(), keyword()) ::
@@ -55,7 +56,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
     with {:ok, local} <- Keyword.fetch(meta, :local) do
       do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
 
-      if !do_not_federate and local and not Activity.local_only?(activity) do
+      if !do_not_federate and local and not Visibility.is_local_public?(activity) do
         activity =
           if object = Keyword.get(meta, :object_data) do
             %{activity | data: Map.put(activity.data, "object", object)}
index faf3bea00c86a892ea93aee17b781b651899760e..46002bec28b34bb4f5a21d47ea073d7d55187a74 100644 (file)
@@ -176,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
     with true <- Config.get!([:instance, :federating]),
          true <- type != "Block" || outgoing_blocks,
-         false <- Activity.local_only?(activity) do
+         false <- Visibility.is_local_public?(activity) do
       Pleroma.Web.Federator.publish(activity)
     end
 
index b3b23a38baa4df987a67204966a67dc4ff545407..2cb5a2bd02b82be26fe7c755bcb246da0a3c6506 100644 (file)
@@ -23,6 +23,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
       Utils.label_in_message?(Pleroma.Constants.as_local_public(), data)
   end
 
+  def is_local_public?(%Object{data: data}), do: is_local_public?(data)
+  def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
+
+  def is_local_public?(data) do
+    Utils.label_in_message?(Pleroma.Constants.as_local_public(), data) and
+      not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
+  end
+
   def is_private?(activity) do
     with false <- is_public?(activity),
          %User{follower_address: follower_address} <-
@@ -118,6 +126,9 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
       Pleroma.Constants.as_public() in cc ->
         "unlisted"
 
+      Pleroma.Constants.as_local_public() in to ->
+        "local"
+
       # this should use the sql for the object's activity
       Enum.any?(to, &String.contains?(&1, "/followers")) ->
         "private"
index e989e4f5f9ab350232b8aa8342c4eff70c8a1082..d7ebde6f6f5c0050e73f8f4c5c3e5433c3e1b345 100644 (file)
@@ -475,10 +475,6 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
           type: :string,
           description:
             "Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`."
-        },
-        local_only: %Schema{
-          type: :boolean,
-          description: "Post the status as local only"
         }
       },
       example: %{
index 831734e27fc0b810ec2b149d62727c6e1aa0e576..633269a92c794e9202858e31112da6c6656c04e6 100644 (file)
@@ -9,6 +9,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do
     title: "VisibilityScope",
     description: "Status visibility",
     type: :string,
-    enum: ["public", "unlisted", "private", "direct", "list"]
+    enum: ["public", "unlisted", "local", "private", "direct", "list"]
   })
 end
index 4df37b69561b0e1a3c91edeac62e08e36d019478..31d9ea677c38c028aa23507ebd4cc68e7364e1cf 100644 (file)
@@ -359,7 +359,7 @@ defmodule Pleroma.Web.CommonAPI do
   def get_visibility(_, _, %Participation{}), do: {"direct", "direct"}
 
   def get_visibility(%{visibility: visibility}, in_reply_to, _)
-      when visibility in ~w{public unlisted private direct},
+      when visibility in ~w{public local unlisted private direct},
       do: {visibility, get_replied_to_visibility(in_reply_to)}
 
   def get_visibility(%{visibility: "list:" <> list_id}, in_reply_to, _) do
index abf6c40d5d4af93c6eaa4234b82fe087890722d2..ae133b54f74afc913cbb2b552c636033a9697403 100644 (file)
@@ -65,8 +65,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
     {Enum.map(participation.recipients, & &1.ap_id), []}
   end
 
-  def get_to_and_cc(%{visibility: "public"} = draft) do
-    to = [public_uri(draft) | draft.mentions]
+  def get_to_and_cc(%{visibility: visibility} = draft) when visibility in ["public", "local"] do
+
+    to =
+      case visibility do
+        "public" -> [Pleroma.Constants.as_public() | draft.mentions]
+        "local" -> [Pleroma.Constants.as_local_public() | draft.mentions]
+      end
+
     cc = [draft.user.follower_address]
 
     if draft.in_reply_to do
@@ -78,7 +84,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
 
   def get_to_and_cc(%{visibility: "unlisted"} = draft) do
     to = [draft.user.follower_address | draft.mentions]
-    cc = [public_uri(draft)]
+    cc = [Pleroma.Constants.as_public()]
 
     if draft.in_reply_to do
       {Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
@@ -103,9 +109,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
 
   def get_to_and_cc(%{visibility: {:list, _}, mentions: mentions}), do: {mentions, []}
 
-  defp public_uri(%{params: %{local_only: true}}), do: Pleroma.Constants.as_local_public()
-  defp public_uri(_), do: Pleroma.Constants.as_public()
-
   def get_addressed_users(_, to) when is_list(to) do
     User.get_ap_ids_by_nicknames(to)
   end
index 0fc78972e055b37b6cbc47a18683180c8a0bb62d..435bcde157552b063deb98a72829c3527e6ea60f 100644 (file)
@@ -368,8 +368,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
         direct_conversation_id: direct_conversation_id,
         thread_muted: thread_muted?,
         emoji_reactions: emoji_reactions,
-        parent_visible: visible_for_user?(reply_to, opts[:for]),
-        local_only: Activity.local_only?(activity)
+        parent_visible: visible_for_user?(reply_to, opts[:for])
       }
     }
   end
index e1dddd21aeae73bf4b8abddd3595f532fe785acd..598ff87de8d2166cc09897c286ec8359a97538f6 100644 (file)
@@ -1256,16 +1256,16 @@ defmodule Pleroma.Web.CommonAPITest do
     end
   end
 
-  describe "with `local_only` enabled" do
+  describe "with `local` visibility" do
     setup do: clear_config([:instance, :federating], true)
 
     test "post" do
       user = insert(:user)
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
-        {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", local_only: true})
+        {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         assert_not_called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1274,13 +1274,13 @@ defmodule Pleroma.Web.CommonAPITest do
       user = insert(:user)
 
       {:ok, %Activity{id: activity_id}} =
-        CommonAPI.post(user, %{status: "#2hu #2HU", local_only: true})
+        CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
         assert {:ok, %Activity{data: %{"deleted_activity_id" => ^activity_id}} = activity} =
                  CommonAPI.delete(activity_id, user)
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         assert_not_called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1290,13 +1290,13 @@ defmodule Pleroma.Web.CommonAPITest do
       other_user = insert(:user)
 
       {:ok, %Activity{id: activity_id}} =
-        CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+        CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
         assert {:ok, %Activity{data: %{"type" => "Announce"}} = activity} =
                  CommonAPI.repeat(activity_id, user)
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1306,7 +1306,7 @@ defmodule Pleroma.Web.CommonAPITest do
       other_user = insert(:user)
 
       {:ok, %Activity{id: activity_id}} =
-        CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+        CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       assert {:ok, _} = CommonAPI.repeat(activity_id, user)
 
@@ -1314,7 +1314,7 @@ defmodule Pleroma.Web.CommonAPITest do
         assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
                  CommonAPI.unrepeat(activity_id, user)
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1323,13 +1323,13 @@ defmodule Pleroma.Web.CommonAPITest do
       user = insert(:user)
       other_user = insert(:user)
 
-      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
         assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
                  CommonAPI.favorite(user, activity.id)
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1338,13 +1338,13 @@ defmodule Pleroma.Web.CommonAPITest do
       user = insert(:user)
       other_user = insert(:user)
 
-      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
         assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1352,13 +1352,13 @@ defmodule Pleroma.Web.CommonAPITest do
     test "react_with_emoji" do
       user = insert(:user)
       other_user = insert(:user)
-      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
         assert {:ok, %Activity{data: %{"type" => "EmojiReact"}} = activity} =
                  CommonAPI.react_with_emoji(activity.id, user, "👍")
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
@@ -1366,7 +1366,7 @@ defmodule Pleroma.Web.CommonAPITest do
     test "unreact_with_emoji" do
       user = insert(:user)
       other_user = insert(:user)
-      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", local_only: true})
+      {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
 
       {:ok, _reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
 
@@ -1374,7 +1374,7 @@ defmodule Pleroma.Web.CommonAPITest do
         assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
                  CommonAPI.unreact_with_emoji(activity.id, user, "👍")
 
-        assert Activity.local_only?(activity)
+        assert Visibility.is_local_public?(activity)
         refute called(Pleroma.Web.Federator.publish(activity))
       end
     end
index ddddd0ea06744851c34e9378441ce9b844465194..d95200f99a2db1f7aa9bbf07c318e77ea4f6ffc6 100644 (file)
@@ -1749,12 +1749,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       |> put_req_header("content-type", "application/json")
       |> post("/api/v1/statuses", %{
         "status" => "cofe",
-        "local_only" => "true"
+        "visibility" => "local"
       })
 
     local = Pleroma.Constants.as_local_public()
 
-    assert %{"content" => "cofe", "id" => id, "pleroma" => %{"local_only" => true}} =
+    assert %{"content" => "cofe", "id" => id, "visibility" => "local"} =
              json_response(conn_one, 200)
 
     assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id)
index 03b0cdf152175255556c35f6421468a566bc0e1e..70d829979bb0cde71f47aec6fee8c92be1158196 100644 (file)
@@ -245,8 +245,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
         direct_conversation_id: nil,
         thread_muted: false,
         emoji_reactions: [],
-        parent_visible: false,
-        local_only: false
+        parent_visible: false
       }
     }