Pinned posts federation Closes #521 See merge request pleroma/pleroma!3312stable
commit
79376b4afb
@ -0,0 +1,77 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
|
||||||
|
embedded_schema do
|
||||||
|
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
||||||
|
field(:target)
|
||||||
|
field(:object, ObjectValidators.ObjectID)
|
||||||
|
field(:actor, ObjectValidators.ObjectID)
|
||||||
|
field(:type)
|
||||||
|
field(:to, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:cc, ObjectValidators.Recipients, default: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_validate(data) do
|
||||||
|
{:ok, actor} = User.get_or_fetch_by_ap_id(data["actor"])
|
||||||
|
|
||||||
|
{:ok, actor} = maybe_refetch_user(actor)
|
||||||
|
|
||||||
|
data
|
||||||
|
|> maybe_fix_data_for_mastodon(actor)
|
||||||
|
|> cast_data()
|
||||||
|
|> validate_data(actor)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_fix_data_for_mastodon(data, actor) do
|
||||||
|
# Mastodon sends pin/unpin objects without id, to, cc fields
|
||||||
|
data
|
||||||
|
|> Map.put_new("id", Pleroma.Web.ActivityPub.Utils.generate_activity_id())
|
||||||
|
|> Map.put_new("to", [Pleroma.Constants.as_public()])
|
||||||
|
|> Map.put_new("cc", [actor.follower_address])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cast_data(data) do
|
||||||
|
cast(%__MODULE__{}, data, __schema__(:fields))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_data(changeset, actor) do
|
||||||
|
changeset
|
||||||
|
|> validate_required([:id, :target, :object, :actor, :type, :to, :cc])
|
||||||
|
|> validate_inclusion(:type, ~w(Add Remove))
|
||||||
|
|> validate_actor_presence()
|
||||||
|
|> validate_collection_belongs_to_actor(actor)
|
||||||
|
|> validate_object_presence()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_collection_belongs_to_actor(changeset, actor) do
|
||||||
|
validate_change(changeset, :target, fn :target, target ->
|
||||||
|
if target == actor.featured_address do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[target: "collection doesn't belong to actor"]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_refetch_user(%User{featured_address: address} = user) when is_binary(address) do
|
||||||
|
{:ok, user}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_refetch_user(%User{ap_id: ap_id}) do
|
||||||
|
Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.AddPinnedObjectsToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:pinned_objects, :map)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,23 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.AddFeaturedAddressToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:featured_address, :string)
|
||||||
|
end
|
||||||
|
|
||||||
|
create(index(:users, [:featured_address]))
|
||||||
|
|
||||||
|
execute("""
|
||||||
|
|
||||||
|
update users set featured_address = concat(ap_id, '/collections/featured') where local = true and featured_address is null;
|
||||||
|
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:featured_address)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,28 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.MovePinnedActivitiesIntoPinnedObjects do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
def up do
|
||||||
|
from(u in User)
|
||||||
|
|> select([u], {u.id, fragment("?.pinned_activities", u)})
|
||||||
|
|> Repo.stream()
|
||||||
|
|> Stream.each(fn {user_id, pinned_activities_ids} ->
|
||||||
|
pinned_activities = Pleroma.Activity.all_by_ids_with_object(pinned_activities_ids)
|
||||||
|
|
||||||
|
pins =
|
||||||
|
Map.new(pinned_activities, fn %{object: %{data: %{"id" => object_id}}} ->
|
||||||
|
{object_id, NaiveDateTime.utc_now()}
|
||||||
|
end)
|
||||||
|
|
||||||
|
from(u in User, where: u.id == ^user_id)
|
||||||
|
|> Repo.update_all(set: [pinned_objects: pins])
|
||||||
|
end)
|
||||||
|
|> Stream.run()
|
||||||
|
end
|
||||||
|
|
||||||
|
def down, do: :noop
|
||||||
|
end
|
@ -0,0 +1,15 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.RemovePinnedActivitiesFromUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:pinned_activities)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:pinned_activities, {:array, :string}, default: [])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://{{domain}}/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language": "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://{{domain}}/users/{{nickname}}/collections/featured",
|
||||||
|
"orderedItems": [
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://{{domain}}/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language": "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actor": "https://{{domain}}/users/{{nickname}}",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://{{domain}}/users/{{nickname}}",
|
||||||
|
"cc": [
|
||||||
|
"https://{{domain}}/users/{{nickname}}/followers"
|
||||||
|
],
|
||||||
|
"content": "",
|
||||||
|
"id": "https://{{domain}}/objects/{{object_id}}",
|
||||||
|
"published": "2021-02-12T15:13:43.915429Z",
|
||||||
|
"sensitive": false,
|
||||||
|
"source": "",
|
||||||
|
"summary": "",
|
||||||
|
"tag": [],
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"type": "Note"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "OrderedCollection"
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"votersCount": "toot:votersCount"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}",
|
||||||
|
"type": "Note",
|
||||||
|
"summary": null,
|
||||||
|
"inReplyTo": null,
|
||||||
|
"published": "2021-02-24T12:40:49Z",
|
||||||
|
"url": "https://example.com/@{{nickname}}/{{status_id}}",
|
||||||
|
"attributedTo": "https://example.com/users/{{nickname}}",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"cc": [
|
||||||
|
"https://example.com/users/{{nickname}}/followers"
|
||||||
|
],
|
||||||
|
"sensitive": false,
|
||||||
|
"atomUri": "https://example.com/users/{{nickname}}/statuses/{{status_id}}",
|
||||||
|
"inReplyToAtomUri": null,
|
||||||
|
"conversation": "tag:example.com,2021-02-24:objectId=15:objectType=Conversation",
|
||||||
|
"content": "<p></p>",
|
||||||
|
"contentMap": {
|
||||||
|
"en": "<p></p>"
|
||||||
|
},
|
||||||
|
"attachment": [],
|
||||||
|
"tag": [],
|
||||||
|
"replies": {
|
||||||
|
"id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies",
|
||||||
|
"type": "Collection",
|
||||||
|
"first": {
|
||||||
|
"type": "CollectionPage",
|
||||||
|
"next": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies?only_other_accounts=true&page=true",
|
||||||
|
"partOf": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies",
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://example.com/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language": "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actor": "https://example.com/users/{{nickname}}",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://example.com/users/{{nickname}}",
|
||||||
|
"cc": [
|
||||||
|
"https://example.com/users/{{nickname}}/followers"
|
||||||
|
],
|
||||||
|
"content": "Content",
|
||||||
|
"context": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f",
|
||||||
|
"conversation": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f",
|
||||||
|
"id": "https://example.com/objects/{{object_id}}",
|
||||||
|
"published": "2019-12-15T22:00:05.279583Z",
|
||||||
|
"sensitive": false,
|
||||||
|
"summary": "",
|
||||||
|
"tag": [],
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"type": "Note"
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"votersCount": "toot:votersCount"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://{{domain}}/users/{{nickname}}/collections/featured",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 0,
|
||||||
|
"orderedItems": []
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://example.com/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language": "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attachment": [],
|
||||||
|
"endpoints": {
|
||||||
|
"oauthAuthorizationEndpoint": "https://example.com/oauth/authorize",
|
||||||
|
"oauthRegistrationEndpoint": "https://example.com/api/v1/apps",
|
||||||
|
"oauthTokenEndpoint": "https://example.com/oauth/token",
|
||||||
|
"sharedInbox": "https://example.com/inbox"
|
||||||
|
},
|
||||||
|
"followers": "https://example.com/users/{{nickname}}/followers",
|
||||||
|
"following": "https://example.com/users/{{nickname}}/following",
|
||||||
|
"icon": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://example.com/media/4e914f5b84e4a259a3f6c2d2edc9ab642f2ab05f3e3d9c52c81fc2d984b3d51e.jpg"
|
||||||
|
},
|
||||||
|
"id": "https://example.com/users/{{nickname}}",
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://example.com/media/f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg?name=f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg"
|
||||||
|
},
|
||||||
|
"inbox": "https://example.com/users/{{nickname}}/inbox",
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"name": "{{nickname}}",
|
||||||
|
"outbox": "https://example.com/users/{{nickname}}/outbox",
|
||||||
|
"preferredUsername": "{{nickname}}",
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://example.com/users/{{nickname}}#main-key",
|
||||||
|
"owner": "https://example.com/users/{{nickname}}",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DLtwGXNZElJyxFGfcVc\nXANhaMadj/iYYQwZjOJTV9QsbtiNBeIK54PJrYuU0/0YIdrvS1iqheX5IwXRhcwa\nhm3ZyLz7XeN9st7FBni4BmZMBtMpxAuYuu5p/jbWy13qAiYOhPreCx0wrWgm/lBD\n9mkgaxIxPooBE0S4ZWEJIDIV1Vft3AWcRUyWW1vIBK0uZzs6GYshbQZB952S0yo4\nFzI1hABGHncH8UvuFauh4EZ8tY7/X5I0pGRnDOcRN1dAht5w5yTA+6r5kebiFQjP\nIzN/eCO/a9Flrj9YGW7HDNtjSOH0A31PLRGlJtJO3yK57dnf5ppyCZGfL4emShQo\ncQIDAQAB\n-----END PUBLIC KEY-----\n\n"
|
||||||
|
},
|
||||||
|
"featured": "https://example.com/users/{{nickname}}/collections/featured",
|
||||||
|
"summary": "your friendly neighborhood pleroma developer<br>I like cute things and distributed systems, and really hate delete and redrafts",
|
||||||
|
"tag": [],
|
||||||
|
"type": "Person",
|
||||||
|
"url": "https://example.com/users/{{nickname}}"
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
defmodule Pleroma.Web.ActivityPub.Transmogrifier.AddRemoveHandlingTest do
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
|
test "it accepts Add/Remove activities" do
|
||||||
|
user =
|
||||||
|
"test/fixtures/users_mock/user.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "lain")
|
||||||
|
|
||||||
|
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"
|
||||||
|
|
||||||
|
object =
|
||||||
|
"test/fixtures/statuses/note.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "lain")
|
||||||
|
|> String.replace("{{object_id}}", object_id)
|
||||||
|
|
||||||
|
object_url = "https://example.com/objects/#{object_id}"
|
||||||
|
|
||||||
|
actor = "https://example.com/users/lain"
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: ^actor
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: user,
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: ^object_url
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: object,
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{method: :get, url: "https://example.com/users/lain/collections/featured"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/users_mock/masto_featured.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{domain}}", "example.com")
|
||||||
|
|> String.replace("{{nickname}}", "lain"),
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f",
|
||||||
|
"actor" => actor,
|
||||||
|
"object" => object_url,
|
||||||
|
"target" => "https://example.com/users/lain/collections/featured",
|
||||||
|
"type" => "Add",
|
||||||
|
"to" => [Pleroma.Constants.as_public()],
|
||||||
|
"cc" => ["https://example.com/users/lain/followers"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
assert activity.data == message
|
||||||
|
user = User.get_cached_by_ap_id(actor)
|
||||||
|
assert user.pinned_objects[object_url]
|
||||||
|
|
||||||
|
remove = %{
|
||||||
|
"id" => "http://localhost:400/objects/d61d6733-e256-4fe1-ab13-1e369789423d",
|
||||||
|
"actor" => actor,
|
||||||
|
"object" => object_url,
|
||||||
|
"target" => "https://example.com/users/lain/collections/featured",
|
||||||
|
"type" => "Remove",
|
||||||
|
"to" => [Pleroma.Constants.as_public()],
|
||||||
|
"cc" => ["https://example.com/users/lain/followers"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(remove)
|
||||||
|
assert activity.data == remove
|
||||||
|
|
||||||
|
user = refresh_record(user)
|
||||||
|
refute user.pinned_objects[object_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Add/Remove activities for remote users without featured address" do
|
||||||
|
user = insert(:user, local: false, domain: "example.com")
|
||||||
|
|
||||||
|
user =
|
||||||
|
user
|
||||||
|
|> Ecto.Changeset.change(featured_address: nil)
|
||||||
|
|> Repo.update!()
|
||||||
|
|
||||||
|
%{host: host} = URI.parse(user.ap_id)
|
||||||
|
|
||||||
|
user_data =
|
||||||
|
"test/fixtures/users_mock/user.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", user.nickname)
|
||||||
|
|
||||||
|
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"
|
||||||
|
|
||||||
|
object =
|
||||||
|
"test/fixtures/statuses/note.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", user.nickname)
|
||||||
|
|> String.replace("{{object_id}}", object_id)
|
||||||
|
|
||||||
|
object_url = "https://#{host}/objects/#{object_id}"
|
||||||
|
|
||||||
|
actor = "https://#{host}/users/#{user.nickname}"
|
||||||
|
|
||||||
|
featured = "https://#{host}/users/#{user.nickname}/collections/featured"
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: ^actor
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: user_data,
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: ^object_url
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: object,
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{method: :get, url: ^featured} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/users_mock/masto_featured.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{domain}}", "#{host}")
|
||||||
|
|> String.replace("{{nickname}}", user.nickname),
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"id" => "https://#{host}/objects/d61d6733-e256-4fe1-ab13-1e369789423f",
|
||||||
|
"actor" => actor,
|
||||||
|
"object" => object_url,
|
||||||
|
"target" => "https://#{host}/users/#{user.nickname}/collections/featured",
|
||||||
|
"type" => "Add",
|
||||||
|
"to" => [Pleroma.Constants.as_public()],
|
||||||
|
"cc" => ["https://#{host}/users/#{user.nickname}/followers"]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
assert activity.data == message
|
||||||
|
user = User.get_cached_by_ap_id(actor)
|
||||||
|
assert user.pinned_objects[object_url]
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in new issue