## 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:
- `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
- `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`
import Ecto.Changeset
import Ecto.Query
- require Pleroma.Constants
-
@type t :: %__MODULE__{}
@type actor :: String.t()
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
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? ->
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()) ::
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)}
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
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} <-
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"
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: %{
title: "VisibilityScope",
description: "Status visibility",
type: :string,
- enum: ["public", "unlisted", "private", "direct", "list"]
+ enum: ["public", "unlisted", "local", "private", "direct", "list"]
})
end
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
{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
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}
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
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
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
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
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
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)
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
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
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
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
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, "👍")
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
|> 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)
direct_conversation_id: nil,
thread_muted: false,
emoji_reactions: [],
- parent_visible: false,
- local_only: false
+ parent_visible: false
}
}