forked from AkkomaGang/akkoma
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
This commit is contained in:
commit
ee35bb5ac2
64 changed files with 1593 additions and 701 deletions
|
@ -15,12 +15,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
|
||||||
- Instance: Add `background_image` to configuration and `/api/v1/instance`
|
- Instance: Add `background_image` to configuration and `/api/v1/instance`
|
||||||
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
|
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
|
||||||
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
|
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
|
||||||
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
||||||
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
|
||||||
- Configuration: Add `:database_config_whitelist` setting to whitelist settings which can be configured from AdminFE.
|
- Configuration: Add `:database_config_whitelist` setting to whitelist settings which can be configured from AdminFE.
|
||||||
|
- Configuration: `filename_display_max_length` option to set filename truncate limit, if filename display enabled (0 = no limit).
|
||||||
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
||||||
- Mix task to create trusted OAuth App.
|
- Mix task to create trusted OAuth App.
|
||||||
- Notifications: Added `follow_request` notification type.
|
- Notifications: Added `follow_request` notification type.
|
||||||
|
|
|
@ -71,7 +71,8 @@
|
||||||
follow_redirect: true,
|
follow_redirect: true,
|
||||||
pool: :upload
|
pool: :upload
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
filename_display_max_length: 30
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,11 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :filename_display_max_length,
|
||||||
|
type: :integer,
|
||||||
|
description: "Set max length of a filename to display. 0 = no limit. Default: 30"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -969,6 +974,13 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :instance_thumbnail,
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"The instance thumbnail image. It will appear in [Pleroma Instances](http://distsn.org/pleroma-instances.html)",
|
||||||
|
suggestions: ["/instance/thumbnail.jpeg"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1112,7 +1124,7 @@
|
||||||
logoMask: true,
|
logoMask: true,
|
||||||
minimalScopesMode: false,
|
minimalScopesMode: false,
|
||||||
noAttachmentLinks: false,
|
noAttachmentLinks: false,
|
||||||
nsfwCensorImage: "",
|
nsfwCensorImage: "/static/img/nsfw.74818f9.png",
|
||||||
postContentType: "text/plain",
|
postContentType: "text/plain",
|
||||||
redirectRootLogin: "/main/friends",
|
redirectRootLogin: "/main/friends",
|
||||||
redirectRootNoLogin: "/main/all",
|
redirectRootNoLogin: "/main/all",
|
||||||
|
@ -1226,7 +1238,7 @@
|
||||||
type: :string,
|
type: :string,
|
||||||
description:
|
description:
|
||||||
"URL of the image to use for hiding NSFW media attachments in the timeline.",
|
"URL of the image to use for hiding NSFW media attachments in the timeline.",
|
||||||
suggestions: ["/static/img/nsfw.png"]
|
suggestions: ["/static/img/nsfw.74818f9.png"]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :postContentType,
|
key: :postContentType,
|
||||||
|
@ -1346,6 +1358,12 @@
|
||||||
suggestions: [
|
suggestions: [
|
||||||
:pleroma_fox_tan
|
:pleroma_fox_tan
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :default_user_avatar,
|
||||||
|
type: :string,
|
||||||
|
description: "URL of the default user avatar.",
|
||||||
|
suggestions: ["/images/avi.png"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -498,6 +498,7 @@ the source code is here: https://github.com/koto-bank/kocaptcha. The default end
|
||||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
||||||
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||||
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
||||||
|
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
||||||
|
|
|
@ -147,6 +147,7 @@ def run(["gen" | rest]) do
|
||||||
"What directory should media uploads go in (when using the local uploader)?",
|
"What directory should media uploads go in (when using the local uploader)?",
|
||||||
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
|
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
|
||||||
)
|
)
|
||||||
|
|> Path.expand()
|
||||||
|
|
||||||
static_dir =
|
static_dir =
|
||||||
get_option(
|
get_option(
|
||||||
|
@ -155,6 +156,7 @@ def run(["gen" | rest]) do
|
||||||
"What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
|
"What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
|
||||||
Pleroma.Config.get([:instance, :static_dir])
|
Pleroma.Config.get([:instance, :static_dir])
|
||||||
)
|
)
|
||||||
|
|> Path.expand()
|
||||||
|
|
||||||
Config.put([:instance, :static_dir], static_dir)
|
Config.put([:instance, :static_dir], static_dir)
|
||||||
|
|
||||||
|
@ -204,7 +206,7 @@ def run(["gen" | rest]) do
|
||||||
shell_info("Writing the postgres script to #{psql_path}.")
|
shell_info("Writing the postgres script to #{psql_path}.")
|
||||||
File.write(psql_path, result_psql)
|
File.write(psql_path, result_psql)
|
||||||
|
|
||||||
write_robots_txt(indexable, template_dir)
|
write_robots_txt(static_dir, indexable, template_dir)
|
||||||
|
|
||||||
shell_info(
|
shell_info(
|
||||||
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
|
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
|
||||||
|
@ -224,15 +226,13 @@ def run(["gen" | rest]) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp write_robots_txt(indexable, template_dir) do
|
defp write_robots_txt(static_dir, indexable, template_dir) do
|
||||||
robots_txt =
|
robots_txt =
|
||||||
EEx.eval_file(
|
EEx.eval_file(
|
||||||
template_dir <> "/robots_txt.eex",
|
template_dir <> "/robots_txt.eex",
|
||||||
indexable: indexable
|
indexable: indexable
|
||||||
)
|
)
|
||||||
|
|
||||||
static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
|
|
||||||
|
|
||||||
unless File.exists?(static_dir) do
|
unless File.exists?(static_dir) do
|
||||||
File.mkdir_p!(static_dir)
|
File.mkdir_p!(static_dir)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,8 +14,10 @@ def new_users(to, users_and_statuses) do
|
||||||
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
|
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
|
||||||
|
|
||||||
logo_url =
|
logo_url =
|
||||||
Pleroma.Web.Endpoint.url() <>
|
Pleroma.Helpers.UriHelper.maybe_add_base(
|
||||||
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
|
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo]),
|
||||||
|
Pleroma.Web.Endpoint.url()
|
||||||
|
)
|
||||||
|
|
||||||
new()
|
new()
|
||||||
|> to({to.name, to.email})
|
|> to({to.name, to.email})
|
||||||
|
|
|
@ -24,4 +24,7 @@ def append_param_if_present(%{} = params, param_name, param_value) do
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
|
||||||
|
def maybe_add_base(uri, _base), do: uri
|
||||||
end
|
end
|
||||||
|
|
|
@ -305,8 +305,13 @@ def invisible?(_), do: false
|
||||||
|
|
||||||
def avatar_url(user, options \\ []) do
|
def avatar_url(user, options \\ []) do
|
||||||
case user.avatar do
|
case user.avatar do
|
||||||
%{"url" => [%{"href" => href} | _]} -> href
|
%{"url" => [%{"href" => href} | _]} ->
|
||||||
_ -> !options[:no_default] && "#{Web.base_url()}/images/avi.png"
|
href
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
unless options[:no_default] do
|
||||||
|
Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -363,36 +363,6 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
|
||||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
|
||||||
def announce(
|
|
||||||
%User{ap_id: _} = user,
|
|
||||||
%Object{data: %{"id" => _}} = object,
|
|
||||||
activity_id \\ nil,
|
|
||||||
local \\ true,
|
|
||||||
public \\ true
|
|
||||||
) do
|
|
||||||
with {:ok, result} <-
|
|
||||||
Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp do_announce(user, object, activity_id, local, public) do
|
|
||||||
with true <- is_announceable?(object, user, public),
|
|
||||||
object <- Object.get_by_id(object.id),
|
|
||||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
|
||||||
{:ok, activity} <- insert(announce_data, local),
|
|
||||||
{:ok, object} <- add_announce_to_object(activity, object),
|
|
||||||
_ <- notify_and_stream(activity),
|
|
||||||
:ok <- maybe_federate(activity) do
|
|
||||||
{:ok, activity, object}
|
|
||||||
else
|
|
||||||
false -> {:error, false}
|
|
||||||
{:error, error} -> Repo.rollback(error)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
|
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||||
{:ok, Activity.t()} | {:error, any()}
|
{:ok, Activity.t()} | {:error, any()}
|
||||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.FederatingPlug
|
alias Pleroma.Web.FederatingPlug
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
|
@ -75,8 +76,8 @@ def user(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def object(conn, %{"uuid" => uuid}) do
|
def object(conn, _) do
|
||||||
with ap_id <- o_status_url(conn, :object, uuid),
|
with ap_id <- Endpoint.url() <> conn.request_path,
|
||||||
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
|
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
|
||||||
{_, true} <- {:public?, Visibility.is_public?(object)} do
|
{_, true} <- {:public?, Visibility.is_public?(object)} do
|
||||||
conn
|
conn
|
||||||
|
@ -101,8 +102,8 @@ def track_object_fetch(conn, object_id) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity(conn, %{"uuid" => uuid}) do
|
def activity(conn, _params) do
|
||||||
with ap_id <- o_status_url(conn, :activity, uuid),
|
with ap_id <- Endpoint.url() <> conn.request_path,
|
||||||
%Activity{} = activity <- Activity.normalize(ap_id),
|
%Activity{} = activity <- Activity.normalize(ap_id),
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
|
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
|
||||||
def emoji_react(actor, object, emoji) do
|
def emoji_react(actor, object, emoji) do
|
||||||
with {:ok, data, meta} <- object_action(actor, object) do
|
with {:ok, data, meta} <- object_action(actor, object) do
|
||||||
|
@ -120,6 +122,29 @@ def like(actor, object) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def announce(actor, object, options \\ []) do
|
||||||
|
public? = Keyword.get(options, :public, false)
|
||||||
|
to = [actor.follower_address, object.data["actor"]]
|
||||||
|
|
||||||
|
to =
|
||||||
|
if public? do
|
||||||
|
[Pleroma.Constants.as_public() | to]
|
||||||
|
else
|
||||||
|
to
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
"id" => Utils.generate_activity_id(),
|
||||||
|
"actor" => actor.ap_id,
|
||||||
|
"object" => object.data["id"],
|
||||||
|
"to" => to,
|
||||||
|
"context" => object.data["context"],
|
||||||
|
"type" => "Announce",
|
||||||
|
"published" => Utils.make_date()
|
||||||
|
}, []}
|
||||||
|
end
|
||||||
|
|
||||||
@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||||
defp object_action(actor, object) do
|
defp object_action(actor, object) do
|
||||||
object_actor = User.get_cached_by_ap_id(object.data["actor"])
|
object_actor = User.get_cached_by_ap_id(object.data["actor"])
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||||
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||||
|
@ -84,6 +85,16 @@ def validate(%{"type" => "Create", "object" => object} = create_activity, meta)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate(%{"type" => "Announce"} = object, meta) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
object
|
||||||
|
|> AnnounceValidator.cast_and_validate()
|
||||||
|
|> Ecto.Changeset.apply_action(:insert) do
|
||||||
|
object = stringify_keys(object |> Map.from_struct())
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
|
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
|
||||||
ChatMessageValidator.cast_and_apply(object)
|
ChatMessageValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
@ -116,7 +127,7 @@ def fetch_actor(object) do
|
||||||
|
|
||||||
def fetch_actor_and_object(object) do
|
def fetch_actor_and_object(object) do
|
||||||
fetch_actor(object)
|
fetch_actor(object)
|
||||||
Object.normalize(object["object"])
|
Object.normalize(object["object"], true)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
@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, autogenerate: {Utils, :generate_context_id, []})
|
||||||
|
field(:to, Types.Recipients, default: [])
|
||||||
|
field(:cc, Types.Recipients, default: [])
|
||||||
|
field(:published, Types.DateTime)
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_data(data_cng) do
|
||||||
|
data_cng
|
||||||
|
|> validate_inclusion(:type, ["Announce"])
|
||||||
|
|> validate_required([:id, :type, :object, :actor, :to, :cc])
|
||||||
|
|> validate_actor_presence()
|
||||||
|
|> validate_object_presence()
|
||||||
|
|> validate_existing_announce()
|
||||||
|
|> validate_announcable()
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_announcable(cng) do
|
||||||
|
with actor when is_binary(actor) <- get_field(cng, :actor),
|
||||||
|
object when is_binary(object) <- get_field(cng, :object),
|
||||||
|
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||||
|
%Object{} = object <- Object.get_cached_by_ap_id(object),
|
||||||
|
false <- Visibility.is_public?(object) do
|
||||||
|
same_actor = object.data["actor"] == actor.ap_id
|
||||||
|
is_public = Pleroma.Constants.as_public() in (get_field(cng, :to) ++ get_field(cng, :cc))
|
||||||
|
|
||||||
|
cond do
|
||||||
|
same_actor && is_public ->
|
||||||
|
cng
|
||||||
|
|> add_error(:actor, "can not announce this object publicly")
|
||||||
|
|
||||||
|
!same_actor ->
|
||||||
|
cng
|
||||||
|
|> add_error(:actor, "can not announce this object")
|
||||||
|
|
||||||
|
true ->
|
||||||
|
cng
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ -> cng
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_existing_announce(cng) do
|
||||||
|
actor = get_field(cng, :actor)
|
||||||
|
object = get_field(cng, :object)
|
||||||
|
|
||||||
|
if actor && object && Utils.get_existing_announce(actor, %{data: %{"id" => object}}) do
|
||||||
|
cng
|
||||||
|
|> add_error(:actor, "already announced this object")
|
||||||
|
|> add_error(:object, "already announced by this actor")
|
||||||
|
else
|
||||||
|
cng
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Pipeline do
|
defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -44,7 +45,7 @@ defp maybe_federate(%Object{}, _), do: {:ok, :not_federated}
|
||||||
|
|
||||||
defp maybe_federate(%Activity{} = activity, meta) do
|
defp maybe_federate(%Activity{} = activity, meta) do
|
||||||
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
||||||
do_not_federate = meta[:do_not_federate]
|
do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
|
||||||
|
|
||||||
if !do_not_federate && local do
|
if !do_not_federate && local do
|
||||||
Federator.publish(activity)
|
Federator.publish(activity)
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@relay_nickname "relay"
|
@relay_nickname "relay"
|
||||||
|
@ -48,11 +49,11 @@ def unfollow(target_instance) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()}
|
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with %User{} = user <- get_actor(),
|
with %User{} = user <- get_actor(),
|
||||||
%Object{} = object <- Object.normalize(activity) do
|
true <- Visibility.is_public?(activity) do
|
||||||
ActivityPub.announce(user, object, nil, true, false)
|
CommonAPI.repeat(activity.id, user)
|
||||||
else
|
else
|
||||||
error -> format_error(error)
|
error -> format_error(error)
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,6 +42,21 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Tasks this handles:
|
||||||
|
# - Add announce to object
|
||||||
|
# - Set up notification
|
||||||
|
# - Stream out the announce
|
||||||
|
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
||||||
|
announced_object = Object.get_by_ap_id(object.data["object"])
|
||||||
|
|
||||||
|
Utils.add_announce_to_object(object, announced_object)
|
||||||
|
|
||||||
|
Notification.create_notifications(object)
|
||||||
|
ActivityPub.stream_out(object)
|
||||||
|
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
|
||||||
def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
|
def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
|
||||||
with undone_object <- Activity.get_by_ap_id(undone_object),
|
with undone_object <- Activity.get_by_ap_id(undone_object),
|
||||||
:ok <- handle_undoing(undone_object) do
|
:ok <- handle_undoing(undone_object) do
|
||||||
|
|
|
@ -672,7 +672,8 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do
|
def handle_incoming(%{"type" => type} = data, _options)
|
||||||
|
when type in ["Like", "EmojiReact", "Announce"] do
|
||||||
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
||||||
{:ok, activity, _meta} <-
|
{:ok, activity, _meta} <-
|
||||||
Pipeline.common_pipeline(data, local: false) do
|
Pipeline.common_pipeline(data, local: false) do
|
||||||
|
@ -682,21 +683,6 @@ def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "E
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(
|
|
||||||
%{"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_embedded_obj_helper(object_id, actor),
|
|
||||||
public <- Visibility.is_public?(data),
|
|
||||||
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
|
|
||||||
{:ok, activity}
|
|
||||||
else
|
|
||||||
_e -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
|
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
|
||||||
data,
|
data,
|
||||||
|
|
|
@ -98,13 +98,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"], admin: true}
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
|
when action in [:list_user_statuses, :list_instance_statuses]
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:statuses"], admin: true}
|
|
||||||
when action in [:status_update, :status_delete]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -136,7 +130,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(AdminAPI.FallbackController)
|
||||||
|
|
||||||
def user_delete(conn, %{"nickname" => nickname}) do
|
def user_delete(conn, %{"nickname" => nickname}) do
|
||||||
user_delete(conn, %{"nicknames" => [nickname]})
|
user_delete(conn, %{"nicknames" => [nickname]})
|
||||||
|
@ -597,16 +591,10 @@ def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params)
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
else
|
else
|
||||||
{:registrations_open, _} ->
|
{:registrations_open, _} ->
|
||||||
errors(
|
|
||||||
conn,
|
|
||||||
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
{:error, "To send invites you need to set the `registrations_open` option to false."}
|
||||||
)
|
|
||||||
|
|
||||||
{:invites_enabled, _} ->
|
{:invites_enabled, _} ->
|
||||||
errors(
|
|
||||||
conn,
|
|
||||||
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
{:error, "To send invites you need to set the `invites_enabled` option to true."}
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -814,71 +802,6 @@ def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
|
|
||||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
|
||||||
local_only = params["local_only"] == "true" || params["local_only"] == true
|
|
||||||
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
|
|
||||||
activities =
|
|
||||||
ActivityPub.fetch_statuses(nil, %{
|
|
||||||
"godmode" => godmode,
|
|
||||||
"local_only" => local_only,
|
|
||||||
"limit" => page_size,
|
|
||||||
"offset" => (page - 1) * page_size,
|
|
||||||
"exclude_reblogs" => !with_reblogs && "true"
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AdminAPI.StatusView)
|
|
||||||
|> render("index.json", %{activities: activities, as: :activity})
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_show(conn, %{"id" => id}) do
|
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id) do
|
|
||||||
conn
|
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|
||||||
|> render("show.json", %{activity: activity})
|
|
||||||
else
|
|
||||||
_ -> errors(conn, {:error, :not_found})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
|
||||||
params =
|
|
||||||
params
|
|
||||||
|> Map.take(["sensitive", "visibility"])
|
|
||||||
|> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end)
|
|
||||||
|
|
||||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
|
||||||
{:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "status_update",
|
|
||||||
actor: admin,
|
|
||||||
subject: activity,
|
|
||||||
sensitive: sensitive,
|
|
||||||
visibility: params[:visibility]
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(MastodonAPI.StatusView)
|
|
||||||
|> render("show.json", %{activity: activity})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|
||||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "status_delete",
|
|
||||||
actor: user,
|
|
||||||
subject_id: id
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, %{})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_log(conn, params) do
|
def list_log(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
@ -904,7 +827,7 @@ def config_descriptions(conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def config_show(conn, %{"only_db" => true}) do
|
def config_show(conn, %{"only_db" => true}) do
|
||||||
with :ok <- configurable_from_database(conn) do
|
with :ok <- configurable_from_database() do
|
||||||
configs = Pleroma.Repo.all(ConfigDB)
|
configs = Pleroma.Repo.all(ConfigDB)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -914,7 +837,7 @@ def config_show(conn, %{"only_db" => true}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def config_show(conn, _params) do
|
def config_show(conn, _params) do
|
||||||
with :ok <- configurable_from_database(conn) do
|
with :ok <- configurable_from_database() do
|
||||||
configs = ConfigDB.get_all_as_keyword()
|
configs = ConfigDB.get_all_as_keyword()
|
||||||
|
|
||||||
merged =
|
merged =
|
||||||
|
@ -953,7 +876,7 @@ def config_show(conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def config_update(conn, %{"configs" => configs}) do
|
def config_update(conn, %{"configs" => configs}) do
|
||||||
with :ok <- configurable_from_database(conn) do
|
with :ok <- configurable_from_database() do
|
||||||
{_errors, results} =
|
{_errors, results} =
|
||||||
configs
|
configs
|
||||||
|> Enum.filter(&whitelisted_config?/1)
|
|> Enum.filter(&whitelisted_config?/1)
|
||||||
|
@ -997,7 +920,7 @@ def config_update(conn, %{"configs" => configs}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def restart(conn, _params) do
|
def restart(conn, _params) do
|
||||||
with :ok <- configurable_from_database(conn) do
|
with :ok <- configurable_from_database() do
|
||||||
Restarter.Pleroma.restart(Config.get(:env), 50)
|
Restarter.Pleroma.restart(Config.get(:env), 50)
|
||||||
|
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
|
@ -1008,14 +931,11 @@ def need_reboot(conn, _params) do
|
||||||
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
|
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp configurable_from_database(conn) do
|
defp configurable_from_database do
|
||||||
if Config.get(:configurable_from_database) do
|
if Config.get(:configurable_from_database) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
errors(
|
|
||||||
conn,
|
|
||||||
{:error, "To use this endpoint you need to enable configuration from database."}
|
{:error, "To use this endpoint you need to enable configuration from database."}
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1159,30 +1079,6 @@ def stats(conn, _) do
|
||||||
|> json(%{"status_visibility" => count})
|
|> json(%{"status_visibility" => count})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp errors(conn, {:error, :not_found}) do
|
|
||||||
conn
|
|
||||||
|> put_status(:not_found)
|
|
||||||
|> json(dgettext("errors", "Not found"))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp errors(conn, {:error, reason}) do
|
|
||||||
conn
|
|
||||||
|> put_status(:bad_request)
|
|
||||||
|> json(reason)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp errors(conn, {:param_cast, _}) do
|
|
||||||
conn
|
|
||||||
|> put_status(:bad_request)
|
|
||||||
|> json(dgettext("errors", "Invalid parameters"))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp errors(conn, _) do
|
|
||||||
conn
|
|
||||||
|> put_status(:internal_server_error)
|
|
||||||
|> json(dgettext("errors", "Something went wrong"))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp page_params(params) do
|
defp page_params(params) do
|
||||||
{get_page(params["page"]), get_page_size(params["page_size"])}
|
{get_page(params["page"]), get_page_size(params["page_size"])}
|
||||||
end
|
end
|
31
lib/pleroma/web/admin_api/controllers/fallback_controller.ex
Normal file
31
lib/pleroma/web/admin_api/controllers/fallback_controller.ex
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.FallbackController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
def call(conn, {:error, :not_found}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> json(%{error: dgettext("errors", "Not found")})
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, {:error, reason}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{error: reason})
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, {:param_cast, _}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(dgettext("errors", "Invalid parameters"))
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, _) do
|
||||||
|
conn
|
||||||
|
|> put_status(:internal_server_error)
|
||||||
|
|> json(%{error: dgettext("errors", "Something went wrong")})
|
||||||
|
end
|
||||||
|
end
|
79
lib/pleroma/web/admin_api/controllers/status_controller.ex
Normal file
79
lib/pleroma/web/admin_api/controllers/status_controller.ex
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.StatusController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"], admin: true} when action in [:index, :show])
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:statuses"], admin: true} when action in [:update, :delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.StatusOperation
|
||||||
|
|
||||||
|
def index(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_statuses(nil, %{
|
||||||
|
"godmode" => params.godmode,
|
||||||
|
"local_only" => params.local_only,
|
||||||
|
"limit" => params.page_size,
|
||||||
|
"offset" => (params.page - 1) * params.page_size,
|
||||||
|
"exclude_reblogs" => not params.with_reblogs
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "index.json", activities: activities, as: :activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(conn, %{id: id}) do
|
||||||
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
|
conn
|
||||||
|
|> put_view(MastodonAPI.StatusView)
|
||||||
|
|> render("show.json", %{activity: activity})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{assigns: %{user: admin}, body_params: params} = conn, %{id: id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "status_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity,
|
||||||
|
sensitive: params[:sensitive],
|
||||||
|
visibility: params[:visibility]
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(MastodonAPI.StatusView)
|
||||||
|
|> render("show.json", %{activity: activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
|
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "status_delete",
|
||||||
|
actor: user,
|
||||||
|
subject_id: id
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
165
lib/pleroma/web/api_spec/operations/admin/status_operation.ex
Normal file
165
lib/pleroma/web/api_spec/operations/admin/status_operation.ex
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
import Pleroma.Web.ApiSpec.StatusOperation, only: [id_param: 0]
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Statuses"],
|
||||||
|
operationId: "AdminAPI.StatusController.index",
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:godmode,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Allows to see private statuses"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:local_only,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Excludes remote statuses"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:with_reblogs,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :boolean, default: false},
|
||||||
|
"Allows to see reblogs"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number of statuses to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of statuses", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: status()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Statuses"],
|
||||||
|
summary: "Show Status",
|
||||||
|
operationId: "AdminAPI.StatusController.show",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Status", "application/json", Status),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Statuses"],
|
||||||
|
summary: "Change the scope of an individual reported status",
|
||||||
|
operationId: "AdminAPI.StatusController.update",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Status", "application/json", Status),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Statuses"],
|
||||||
|
summary: "Delete an individual reported status",
|
||||||
|
operationId: "AdminAPI.StatusController.delete",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => empty_object_response(),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status do
|
||||||
|
%Schema{
|
||||||
|
anyOf: [
|
||||||
|
Status,
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
account: %Schema{allOf: [Account, admin_account()]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp admin_account do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
avatar: %Schema{type: :string},
|
||||||
|
nickname: %Schema{type: :string},
|
||||||
|
display_name: %Schema{type: :string},
|
||||||
|
deactivated: %Schema{type: :boolean},
|
||||||
|
local: %Schema{type: :boolean},
|
||||||
|
roles: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
admin: %Schema{type: :boolean},
|
||||||
|
moderator: %Schema{type: :boolean}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tags: %Schema{type: :string},
|
||||||
|
confirmation_pending: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
sensitive: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Mark status and attached media as sensitive?"
|
||||||
|
},
|
||||||
|
visibility: VisibilityScope
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"visibility" => "private",
|
||||||
|
"sensitive" => "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
|
||||||
|
@ -46,7 +47,8 @@ def create_operation do
|
||||||
security: [%{"oAuth" => ["write:statuses"]}],
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
operationId: "EmojiReactionController.create",
|
operationId: "EmojiReactionController.create",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Status", "application/json", Status)
|
200 => Operation.response("Status", "application/json", Status),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
|
||||||
alias Pleroma.Web.ApiSpec.NotificationOperation
|
alias Pleroma.Web.ApiSpec.NotificationOperation
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
apply(__MODULE__, operation, [])
|
apply(__MODULE__, operation, [])
|
||||||
|
@ -17,10 +19,14 @@ def mark_as_read_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Notifications"],
|
tags: ["Notifications"],
|
||||||
summary: "Mark notifications as read. Query parameters are mutually exclusive.",
|
summary: "Mark notifications as read. Query parameters are mutually exclusive.",
|
||||||
parameters: [
|
requestBody:
|
||||||
Operation.parameter(:id, :query, :string, "A single notification ID to read"),
|
request_body("Parameters", %Schema{
|
||||||
Operation.parameter(:max_id, :query, :string, "Read all notifications up to this id")
|
type: :object,
|
||||||
],
|
properties: %{
|
||||||
|
id: %Schema{type: :integer, description: "A single notification ID to read"},
|
||||||
|
max_id: %Schema{type: :integer, description: "Read all notifications up to this ID"}
|
||||||
|
}
|
||||||
|
}),
|
||||||
security: [%{"oAuth" => ["write:notifications"]}],
|
security: [%{"oAuth" => ["write:notifications"]}],
|
||||||
operationId: "PleromaAPI.NotificationController.mark_as_read",
|
operationId: "PleromaAPI.NotificationController.mark_as_read",
|
||||||
responses: %{
|
responses: %{
|
||||||
|
|
|
@ -487,7 +487,7 @@ defp create_request do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp id_param do
|
def id_param do
|
||||||
Operation.parameter(:id, :path, FlakeID, "Status ID",
|
Operation.parameter(:id, :path, FlakeID, "Status ID",
|
||||||
example: "9umDrYheeY451cQnEe",
|
example: "9umDrYheeY451cQnEe",
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -167,18 +167,19 @@ def delete(activity_id, user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def repeat(id, user, params \\ %{}) do
|
def repeat(id, user, params \\ %{}) do
|
||||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
|
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||||
object = Object.normalize(activity)
|
object = %Object{} <- Object.normalize(activity, false),
|
||||||
announce_activity = Utils.get_existing_announce(user.ap_id, object)
|
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
||||||
public = public_announce?(object, params)
|
public = public_announce?(object, params),
|
||||||
|
{:ok, announce, _} <- Builder.announce(user, object, public: public),
|
||||||
|
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:existing_announce, %Activity{} = announce} ->
|
||||||
|
{:ok, announce}
|
||||||
|
|
||||||
if announce_activity do
|
_ ->
|
||||||
{:ok, announce_activity, object}
|
{:error, :not_found}
|
||||||
else
|
|
||||||
ActivityPub.announce(user, object, nil, true, public)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
_ -> {:error, :not_found}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,8 @@ def get_to_and_cc(user, mentioned_users, inReplyTo, "private", _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do
|
def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do
|
||||||
if inReplyTo do
|
# If the OP is a DM already, add the implicit actor.
|
||||||
|
if inReplyTo && Visibility.is_direct?(inReplyTo) do
|
||||||
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
|
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
|
||||||
else
|
else
|
||||||
{mentioned_users, []}
|
{mentioned_users, []}
|
||||||
|
@ -395,10 +396,12 @@ def to_masto_date(date) when is_binary(date) do
|
||||||
def to_masto_date(_), do: ""
|
def to_masto_date(_), do: ""
|
||||||
|
|
||||||
defp shortname(name) do
|
defp shortname(name) do
|
||||||
if String.length(name) < 30 do
|
with max_length when max_length > 0 <-
|
||||||
name
|
Config.get([Pleroma.Upload, :filename_display_max_length], 30),
|
||||||
|
true <- String.length(name) > max_length do
|
||||||
|
String.slice(name, 0..max_length) <> "…"
|
||||||
else
|
else
|
||||||
String.slice(name, 0..30) <> "…"
|
_ -> name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -467,6 +470,8 @@ def maybe_notify_subscribers(
|
||||||
|> Enum.map(& &1.ap_id)
|
|> Enum.map(& &1.ap_id)
|
||||||
|
|
||||||
recipients ++ subscriber_ids
|
recipients ++ subscriber_ids
|
||||||
|
else
|
||||||
|
_e -> recipients
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -478,6 +483,8 @@ def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = ac
|
||||||
|> User.get_followers()
|
|> User.get_followers()
|
||||||
|> Enum.map(& &1.ap_id)
|
|> Enum.map(& &1.ap_id)
|
||||||
|> Enum.concat(recipients)
|
|> Enum.concat(recipients)
|
||||||
|
else
|
||||||
|
_e -> recipients
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,7 @@ def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
|
|
||||||
@doc "POST /api/v1/statuses/:id/reblog"
|
@doc "POST /api/v1/statuses/:id/reblog"
|
||||||
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
|
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
|
||||||
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),
|
with {:ok, announce} <- CommonAPI.repeat(ap_id_or_id, user, params),
|
||||||
%Activity{} = announce <- Activity.normalize(announce.data) do
|
%Activity{} = announce <- Activity.normalize(announce.data) do
|
||||||
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
|
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,7 +23,7 @@ def render("show.json", _) do
|
||||||
streaming_api: Pleroma.Web.Endpoint.websocket_url()
|
streaming_api: Pleroma.Web.Endpoint.websocket_url()
|
||||||
},
|
},
|
||||||
stats: Pleroma.Stats.get_stats(),
|
stats: Pleroma.Stats.get_stats(),
|
||||||
thumbnail: Pleroma.Web.base_url() <> "/instance/thumbnail.jpeg",
|
thumbnail: instance_thumbnail(),
|
||||||
languages: ["en"],
|
languages: ["en"],
|
||||||
registrations: Keyword.get(instance, :registrations_open),
|
registrations: Keyword.get(instance, :registrations_open),
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
|
@ -88,4 +88,9 @@ def federation do
|
||||||
end
|
end
|
||||||
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp instance_thumbnail do
|
||||||
|
Pleroma.Config.get([:instance, :instance_thumbnail]) ||
|
||||||
|
"#{Pleroma.Web.base_url()}/instance/thumbnail.jpeg"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,13 +32,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
def object(%{assigns: %{format: format}} = conn, _params)
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
def object(%{assigns: %{format: format}} = conn, _params) do
|
||||||
with id <- o_status_url(conn, :object, uuid),
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <-
|
{_, %Activity{} = activity} <-
|
||||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
|
@ -54,13 +54,13 @@ def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
def activity(%{assigns: %{format: format}} = conn, _params)
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
ActivityPubController.call(conn, :activity)
|
ActivityPubController.call(conn, :activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
def activity(%{assigns: %{format: format}} = conn, _params) do
|
||||||
with id <- o_status_url(conn, :activity, uuid),
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
case format do
|
case format do
|
||||||
|
|
|
@ -22,6 +22,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.EmojiReactionOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.EmojiReactionOperation
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
|
def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||||
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
|
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
|
||||||
|
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation
|
||||||
|
|
||||||
def mark_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
|
def mark_as_read(%{assigns: %{user: user}, body_params: %{id: notification_id}} = conn, _) do
|
||||||
with {:ok, notification} <- Notification.read_one(user, notification_id) do
|
with {:ok, notification} <- Notification.read_one(user, notification_id) do
|
||||||
render(conn, "show.json", notification: notification, for: user)
|
render(conn, "show.json", notification: notification, for: user)
|
||||||
else
|
else
|
||||||
|
@ -25,7 +25,7 @@ def mark_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_as_read(%{assigns: %{user: user}} = conn, %{max_id: max_id}) do
|
def mark_as_read(%{assigns: %{user: user}, body_params: %{max_id: max_id}} = conn, _) do
|
||||||
notifications =
|
notifications =
|
||||||
user
|
user
|
||||||
|> Notification.set_read_up_to(max_id)
|
|> Notification.set_read_up_to(max_id)
|
||||||
|
|
|
@ -189,10 +189,10 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
||||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
||||||
|
|
||||||
get("/statuses/:id", AdminAPIController, :status_show)
|
get("/statuses/:id", StatusController, :show)
|
||||||
put("/statuses/:id", AdminAPIController, :status_update)
|
put("/statuses/:id", StatusController, :update)
|
||||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
delete("/statuses/:id", StatusController, :delete)
|
||||||
get("/statuses", AdminAPIController, :list_statuses)
|
get("/statuses", StatusController, :index)
|
||||||
|
|
||||||
get("/config", AdminAPIController, :config_show)
|
get("/config", AdminAPIController, :config_show)
|
||||||
post("/config", AdminAPIController, :config_update)
|
post("/config", AdminAPIController, :config_update)
|
||||||
|
@ -564,6 +564,10 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||||
|
|
||||||
|
# Mastodon compatibility routes
|
||||||
|
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||||
|
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||||
|
|
||||||
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
||||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||||
|
|
||||||
|
|
1
priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE
Normal file
1
priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE
Normal file
|
@ -0,0 +1 @@
|
||||||
|
If you are an instance admin and you want to modify the instace static files, this is probably not the right place to do it. This directory is checked in version control, so don't be surprised if you get merge conflicts after modifying anything here. Please use instance static directory instead, it has the same directory structure and files placed there will override files placed here. See https://docs.pleroma.social/backend/configuration/static_dir/ for more info
|
|
@ -1 +1,88 @@
|
||||||
{"@context":["https://www.w3.org/ns/activitystreams","https://puckipedia.com/-/context"],"actor":{"endpoints":"https://puckipedia.com/#endpoints","followers":"https://puckipedia.com/followers","following":"https://puckipedia.com/following","icon":{"mediaType":"image/png","type":"Image","url":"https://puckipedia.com/images/avatar.png"},"id":"https://puckipedia.com/","inbox":"https://puckipedia.com/inbox","kroeg:blocks":{"id":"https://puckipedia.com/blocks"},"liked":"https://puckipedia.com/liked","manuallyApprovesFollowers":false,"name":"HACKER TEEN PUCKIPEDIA 👩💻","outbox":"https://puckipedia.com/outbox","preferredUsername":"puckipedia","publicKey":{"id":"https://puckipedia.com/#key","owner":"https://puckipedia.com/","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----","type":[]},"summary":"<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>","type":"Person","updated":"2017-12-19T16:56:29.7576707+00:00"},"cc":"http://mastodon.example.org/users/admin","id":"https://puckipedia.com/cc56a9658e","object":{"as:sensitive":false,"attributedTo":{"endpoints":{"sharedInbox":"https://mastodon.social/inbox","type":[]},"followers":"http://mastodon.example.org/users/admin/followers","following":"http://mastodon.example.org/users/admin/following","icon":{"mediaType":"image/png","type":"Image","url":"https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"},"id":"http://mastodon.example.org/users/admin","inbox":"http://mastodon.example.org/users/admin/inbox","manuallyApprovesFollowers":{"@value":"False","type":"xsd:boolean"},"name":"","outbox":"http://mastodon.example.org/users/admin/outbox","preferredUsername":"revenant","publicKey":{"id":"http://mastodon.example.org/users/admin#main-key","owner":"http://mastodon.example.org/users/admin","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n","type":[]},"summary":"<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"http://mastodon.example.org/users/admin/followers","content":"<p>the name's jond (jeans bond)</p>","contentMap":{"en":"<p>the name's jond (jeans bond)</p>"},"conversation":"tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation","id":"http://mastodon.example.org/users/admin/statuses/100787282858396771","ostatus:atomUri":"http://mastodon.example.org/users/admin/statuses/100787282858396771","published":"2018-09-25T16:11:29Z","to":"https://www.w3.org/ns/activitystreams#Public","type":"Note","url":"https://mastodon.social/@revenant/100787282858396771"},"to":["https://www.w3.org/ns/activitystreams#Public","https://puckipedia.com/followers"],"type":"Announce"}
|
{
|
||||||
|
"@context" : [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://puckipedia.com/-/context"
|
||||||
|
],
|
||||||
|
"actor" : {
|
||||||
|
"endpoints" : "https://puckipedia.com/#endpoints",
|
||||||
|
"followers" : "https://puckipedia.com/followers",
|
||||||
|
"following" : "https://puckipedia.com/following",
|
||||||
|
"icon" : {
|
||||||
|
"mediaType" : "image/png",
|
||||||
|
"type" : "Image",
|
||||||
|
"url" : "https://puckipedia.com/images/avatar.png"
|
||||||
|
},
|
||||||
|
"id" : "https://puckipedia.com/",
|
||||||
|
"inbox" : "https://puckipedia.com/inbox",
|
||||||
|
"kroeg:blocks" : {
|
||||||
|
"id" : "https://puckipedia.com/blocks"
|
||||||
|
},
|
||||||
|
"liked" : "https://puckipedia.com/liked",
|
||||||
|
"manuallyApprovesFollowers" : false,
|
||||||
|
"name" : "HACKER TEEN PUCKIPEDIA ð©âð»",
|
||||||
|
"outbox" : "https://puckipedia.com/outbox",
|
||||||
|
"preferredUsername" : "puckipedia",
|
||||||
|
"publicKey" : {
|
||||||
|
"id" : "https://puckipedia.com/#key",
|
||||||
|
"owner" : "https://puckipedia.com/",
|
||||||
|
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvN05xIcFE0Qgany7Rht4\n0ZI5wu++IT7K5iSqRimBYkpoeHbVcT9RFlW+aWH/QJJW/YgZ7+LMr8AMCrKrwSpS\nCndyrpx4O4lZ3FNRLu7tbklh01rGZfE6R1SFfYBpvMvImc9nYT6iezYDbv6NkHku\no3aVhjql216XlA0OhIrqQme9sAdrLbjbMrTUS8douCTkDOX+JFj1ghHCqdYEMZJI\nOY9kovtgnqyxFLm0RsPGsO1+g/OVojqG+VqHz6O2lceaTVQLlnZ4gOhLVG1tVsA2\nRfXQK+R/VgXncYE+BlQVd/tcdGAz7CDL7PP3rP65gmARnafhGR96cCOi/KzlAXSO\nMwIDAQAB\n-----END PUBLIC KEY-----",
|
||||||
|
"type" : []
|
||||||
|
},
|
||||||
|
"summary" : "<p>federated hacker teen<br/>\n[<a href=\"https://pronoun.is/she\">she</a>/<a href=\"https://pronoun.is/they\">they</a>]</p>",
|
||||||
|
"type" : "Person",
|
||||||
|
"updated" : "2017-12-19T16:56:29.7576707+00:00"
|
||||||
|
},
|
||||||
|
"cc" : "http://mastodon.example.org/users/admin",
|
||||||
|
"id" : "https://puckipedia.com/cc56a9658e",
|
||||||
|
"object" : {
|
||||||
|
"as:sensitive" : false,
|
||||||
|
"attributedTo" : {
|
||||||
|
"endpoints" : {
|
||||||
|
"sharedInbox" : "https://mastodon.social/inbox",
|
||||||
|
"type" : []
|
||||||
|
},
|
||||||
|
"followers" : "http://mastodon.example.org/users/admin/followers",
|
||||||
|
"following" : "http://mastodon.example.org/users/admin/following",
|
||||||
|
"icon" : {
|
||||||
|
"mediaType" : "image/png",
|
||||||
|
"type" : "Image",
|
||||||
|
"url" : "https://files.mastodon.social/accounts/avatars/000/015/163/original/70ca6c52b01ca913.png"
|
||||||
|
},
|
||||||
|
"id" : "http://mastodon.example.org/users/admin",
|
||||||
|
"inbox" : "http://mastodon.example.org/users/admin/inbox",
|
||||||
|
"manuallyApprovesFollowers" : {
|
||||||
|
"@value" : "False",
|
||||||
|
"type" : "xsd:boolean"
|
||||||
|
},
|
||||||
|
"name" : "",
|
||||||
|
"outbox" : "http://mastodon.example.org/users/admin/outbox",
|
||||||
|
"preferredUsername" : "revenant",
|
||||||
|
"publicKey" : {
|
||||||
|
"id" : "http://mastodon.example.org/users/admin#main-key",
|
||||||
|
"owner" : "http://mastodon.example.org/users/admin",
|
||||||
|
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0gEN3wPW7gkE2gQqnmfB\n1ychjmFIf2LIwY0oCJLiGE/xpZrUKoq+eWH30AP7mATw4LD0gOYABL/ijqPUrPqR\nDXLL+0CqMP8HsZKvRlj9KArMK3YtNiSGGj2U7iReiRrD7nJzjJlsjjJXflLZhZ7/\nenSv1CcaeK8tB0PoAgShy/MyfhPF7WI5/Zm9DmmDQFvUEnDYKXAf/vG/IWw1EyMC\nkbaEYJeIowQU3GsbPxzRGI22bQtfotm431Ch2MbNo+kyzmYVFLAVoSGNMzvJwOPg\nTxLIIBeQXG7MinRyK887yPKhxhcALea4yCcALaa+3jPE7yqwIKYwTHtSlblsHDAo\nmQIDAQAB\n-----END PUBLIC KEY-----\n",
|
||||||
|
"type" : []
|
||||||
|
},
|
||||||
|
"summary" : "<p>neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net</p>",
|
||||||
|
"type" : "Person",
|
||||||
|
"url" : "https://mastodon.social/@revenant"
|
||||||
|
},
|
||||||
|
"cc" : "http://mastodon.example.org/users/admin/followers",
|
||||||
|
"content" : "<p>the name's jond (jeans bond)</p>",
|
||||||
|
"contentMap" : {
|
||||||
|
"en" : "<p>the name's jond (jeans bond)</p>"
|
||||||
|
},
|
||||||
|
"conversation" : "tag:mastodon.social,2018-09-25:objectId=55659382:objectType=Conversation",
|
||||||
|
"id" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
|
||||||
|
"ostatus:atomUri" : "http://mastodon.example.org/users/admin/statuses/100787282858396771",
|
||||||
|
"published" : "2018-09-25T16:11:29Z",
|
||||||
|
"to" : "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"type" : "Note",
|
||||||
|
"url" : "https://mastodon.social/@revenant/100787282858396771"
|
||||||
|
},
|
||||||
|
"to" : [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://puckipedia.com/followers"
|
||||||
|
],
|
||||||
|
"type" : "Announce"
|
||||||
|
}
|
||||||
|
|
50
test/fixtures/mastodon-note-object.json
vendored
50
test/fixtures/mastodon-note-object.json
vendored
|
@ -1,9 +1,45 @@
|
||||||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":"as:movedTo","Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji"}],"id":"http://mastodon.example.org/users/admin/statuses/99541947525187367","type":"Note","summary":null,"content":"\u003cp\u003eyeah.\u003c/p\u003e","inReplyTo":null,"published":"2018-02-17T17:46:20Z","url":"http://mastodon.example.org/@admin/99541947525187367","attributedTo":"http://mastodon.example.org/users/admin","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["http://mastodon.example.org/users/admin/followers"],"sensitive":false,"atomUri":"http://mastodon.example.org/users/admin/statuses/99541947525187367","inReplyToAtomUri":null,"conversation":"tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation","tag":[],
|
{
|
||||||
"attachment": [
|
"@context" : [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
{
|
{
|
||||||
"url": "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
|
"Emoji" : "toot:Emoji",
|
||||||
"type": "Document",
|
"Hashtag" : "as:Hashtag",
|
||||||
"name": null,
|
"atomUri" : "ostatus:atomUri",
|
||||||
"mediaType": "image/jpeg"
|
"conversation" : "ostatus:conversation",
|
||||||
|
"inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
|
||||||
|
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
|
||||||
|
"movedTo" : "as:movedTo",
|
||||||
|
"ostatus" : "http://ostatus.org#",
|
||||||
|
"sensitive" : "as:sensitive",
|
||||||
|
"toot" : "http://joinmastodon.org/ns#"
|
||||||
}
|
}
|
||||||
]}
|
],
|
||||||
|
"atomUri" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
|
||||||
|
"attachment" : [
|
||||||
|
{
|
||||||
|
"mediaType" : "image/jpeg",
|
||||||
|
"name" : null,
|
||||||
|
"type" : "Document",
|
||||||
|
"url" : "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributedTo" : "http://mastodon.example.org/users/admin",
|
||||||
|
"cc" : [
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
],
|
||||||
|
"content" : "<p>yeah.</p>",
|
||||||
|
"conversation" : "tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation",
|
||||||
|
"id" : "http://mastodon.example.org/users/admin/statuses/99541947525187367",
|
||||||
|
"inReplyTo" : null,
|
||||||
|
"inReplyToAtomUri" : null,
|
||||||
|
"published" : "2018-02-17T17:46:20Z",
|
||||||
|
"sensitive" : false,
|
||||||
|
"summary" : null,
|
||||||
|
"tag" : [],
|
||||||
|
"to" : [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"type" : "Note",
|
||||||
|
"url" : "http://mastodon.example.org/@admin/99541947525187367"
|
||||||
|
}
|
||||||
|
|
|
@ -648,7 +648,7 @@ test "it does not send notification to mentioned users in announces" do
|
||||||
status: "hey @#{other_user.nickname}!"
|
status: "hey @#{other_user.nickname}!"
|
||||||
})
|
})
|
||||||
|
|
||||||
{:ok, activity_two, _} = CommonAPI.repeat(activity_one.id, third_user)
|
{:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
|
||||||
|
|
||||||
{enabled_receivers, _disabled_receivers} =
|
{enabled_receivers, _disabled_receivers} =
|
||||||
Notification.get_notified_from_activity(activity_two)
|
Notification.get_notified_from_activity(activity_two)
|
||||||
|
@ -778,7 +778,7 @@ test "repeating an activity results in 1 notification, then 0 if the activity is
|
||||||
|
|
||||||
assert Enum.empty?(Notification.for_user(user))
|
assert Enum.empty?(Notification.for_user(user))
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
assert length(Notification.for_user(user)) == 1
|
assert length(Notification.for_user(user)) == 1
|
||||||
|
|
||||||
|
@ -795,7 +795,7 @@ test "repeating an activity results in 1 notification, then 0 if the activity is
|
||||||
|
|
||||||
assert Enum.empty?(Notification.for_user(user))
|
assert Enum.empty?(Notification.for_user(user))
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
assert length(Notification.for_user(user)) == 1
|
assert length(Notification.for_user(user)) == 1
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ test "running gen" do
|
||||||
"--uploads-dir",
|
"--uploads-dir",
|
||||||
"test/uploads",
|
"test/uploads",
|
||||||
"--static-dir",
|
"--static-dir",
|
||||||
"instance/static/"
|
"./test/../test/instance/static/"
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ test "running gen" do
|
||||||
assert generated_config =~ "configurable_from_database: true"
|
assert generated_config =~ "configurable_from_database: true"
|
||||||
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
|
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
|
||||||
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
|
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
|
||||||
|
assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generated_setup_psql do
|
defp generated_setup_psql do
|
||||||
|
|
|
@ -91,6 +91,7 @@ test "user is not created" do
|
||||||
|
|
||||||
describe "running rm" do
|
describe "running rm" do
|
||||||
test "user is deleted" do
|
test "user is deleted" do
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
with_mock Pleroma.Web.Federator,
|
with_mock Pleroma.Web.Federator,
|
||||||
|
@ -108,8 +109,10 @@ test "user is deleted" do
|
||||||
|
|
||||||
test "a remote user's create activity is deleted when the object has been pruned" do
|
test "a remote user's create activity is deleted when the object has been pruned" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, post} = CommonAPI.post(user, %{status: "uguu"})
|
{:ok, post} = CommonAPI.post(user, %{status: "uguu"})
|
||||||
|
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
object = Object.normalize(post)
|
object = Object.normalize(post)
|
||||||
Object.prune(object)
|
Object.prune(object)
|
||||||
|
|
||||||
|
|
|
@ -992,7 +992,7 @@ test "works for announces" do
|
||||||
user = insert(:user, local: true)
|
user = insert(:user, local: true)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
|
{:ok, activity} = CommonAPI.post(actor, %{status: "hello"})
|
||||||
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, announce} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
recipients = User.get_recipients_from_activity(announce)
|
recipients = User.get_recipients_from_activity(announce)
|
||||||
|
|
||||||
|
@ -1147,7 +1147,7 @@ test "it deactivates a user, all follow relationships and all activities", %{use
|
||||||
|
|
||||||
{:ok, like} = CommonAPI.favorite(user, activity_two.id)
|
{:ok, like} = CommonAPI.favorite(user, activity_two.id)
|
||||||
{:ok, like_two} = CommonAPI.favorite(follower, activity.id)
|
{:ok, like_two} = CommonAPI.favorite(follower, activity.id)
|
||||||
{:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
|
{:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
|
||||||
|
|
||||||
{:ok, job} = User.delete(user)
|
{:ok, job} = User.delete(user)
|
||||||
{:ok, _user} = ObanHelpers.perform(job)
|
{:ok, _user} = ObanHelpers.perform(job)
|
||||||
|
@ -1777,4 +1777,16 @@ test "Notifications are updated", %{user: user} do
|
||||||
assert result.email_notifications["digest"] == false
|
assert result.email_notifications["digest"] == false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "avatar fallback" do
|
||||||
|
user = insert(:user)
|
||||||
|
assert User.avatar_url(user) =~ "/images/avi.png"
|
||||||
|
|
||||||
|
Pleroma.Config.put([:assets, :default_user_avatar], "avatar.png")
|
||||||
|
|
||||||
|
user = User.get_cached_by_nickname_or_id(user.nickname)
|
||||||
|
assert User.avatar_url(user) =~ "avatar.png"
|
||||||
|
|
||||||
|
assert User.avatar_url(user, no_default: true) == nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
use Oban.Testing, repo: Pleroma.Repo
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
import Pleroma.Factory
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Delivery
|
alias Pleroma.Delivery
|
||||||
|
@ -14,13 +13,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.ObjectView
|
alias Pleroma.Web.ActivityPub.ObjectView
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Workers.ReceiverWorker
|
alias Pleroma.Workers.ReceiverWorker
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
setup_all do
|
setup_all do
|
||||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
:ok
|
:ok
|
||||||
|
@ -168,6 +173,60 @@ test "it requires authentication if instance is NOT federating", %{
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "mastodon compatibility routes" do
|
||||||
|
test "it returns a json representation of the object with accept application/json", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
{:ok, object} =
|
||||||
|
%{
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "hey",
|
||||||
|
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
|
||||||
|
"actor" => Endpoint.url() <> "/users/raymoo",
|
||||||
|
"to" => [Pleroma.Constants.as_public()]
|
||||||
|
}
|
||||||
|
|> Object.create()
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/users/raymoo/statuses/999999999")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns a json representation of the activity with accept application/json", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
{:ok, object} =
|
||||||
|
%{
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "hey",
|
||||||
|
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
|
||||||
|
"actor" => Endpoint.url() <> "/users/raymoo",
|
||||||
|
"to" => [Pleroma.Constants.as_public()]
|
||||||
|
}
|
||||||
|
|> Object.create()
|
||||||
|
|
||||||
|
{:ok, activity, _} =
|
||||||
|
%{
|
||||||
|
"id" => object.data["id"] <> "/activity",
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => object.data["id"],
|
||||||
|
"actor" => object.data["actor"],
|
||||||
|
"to" => object.data["to"]
|
||||||
|
}
|
||||||
|
|> ActivityPub.persist(local: true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/users/raymoo/statuses/999999999/activity")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "/objects/:uuid" do
|
describe "/objects/:uuid" do
|
||||||
test "it returns a json representation of the object with accept application/json", %{
|
test "it returns a json representation of the object with accept application/json", %{
|
||||||
conn: conn
|
conn: conn
|
||||||
|
|
|
@ -537,7 +537,7 @@ test "doesn't return blocked activities" do
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
|
|
||||||
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
|
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
|
||||||
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Activity.get_by_id(activity_three.id)
|
activity_three = Activity.get_by_id(activity_three.id)
|
||||||
|
|
||||||
|
@ -592,7 +592,7 @@ test "doesn't return announce activities concerning blocked users" do
|
||||||
|
|
||||||
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
|
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
|
||||||
|
|
||||||
{:ok, activity_three, _} = CommonAPI.repeat(activity_two.id, friend)
|
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
|
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
|
||||||
|
@ -618,7 +618,7 @@ test "doesn't return activities from blocked domains" do
|
||||||
|
|
||||||
followed_user = insert(:user)
|
followed_user = insert(:user)
|
||||||
ActivityPub.follow(user, followed_user)
|
ActivityPub.follow(user, followed_user)
|
||||||
{:ok, repeat_activity, _} = CommonAPI.repeat(activity.id, followed_user)
|
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
|
||||||
|
@ -651,7 +651,7 @@ test "does return activities from followed users on blocked domains" do
|
||||||
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
|
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
|
||||||
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
|
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
|
||||||
bad_activity = insert(:note_activity, %{note: bad_note})
|
bad_activity = insert(:note_activity, %{note: bad_note})
|
||||||
{:ok, repeat_activity, _} = CommonAPI.repeat(bad_activity.id, domain_user)
|
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
ActivityPub.fetch_activities([], %{"blocking_user" => blocker, "skip_preload" => true})
|
||||||
|
@ -699,7 +699,7 @@ test "doesn't return muted activities" do
|
||||||
|
|
||||||
activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
|
activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
|
||||||
{:ok, _user_relationships} = User.mute(user, activity_three_actor)
|
{:ok, _user_relationships} = User.mute(user, activity_three_actor)
|
||||||
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
|
||||||
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
|
||||||
activity_three = Activity.get_by_id(activity_three.id)
|
activity_three = Activity.get_by_id(activity_three.id)
|
||||||
|
|
||||||
|
@ -749,7 +749,7 @@ test "does include announces on request" do
|
||||||
|
|
||||||
{:ok, user} = User.follow(user, booster)
|
{:ok, user} = User.follow(user, booster)
|
||||||
|
|
||||||
{:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
|
{:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
|
||||||
|
|
||||||
[announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
|
[announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
|
||||||
|
|
||||||
|
@ -846,7 +846,7 @@ test "doesn't return reblogs for users for whom reblogs have been muted" do
|
||||||
booster = insert(:user)
|
booster = insert(:user)
|
||||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||||
|
|
||||||
{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
|
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
||||||
|
|
||||||
|
@ -860,7 +860,7 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
|
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
|
||||||
|
|
||||||
{:ok, activity, _} = CommonAPI.repeat(activity.id, booster)
|
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||||
|
|
||||||
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
|
||||||
|
|
||||||
|
@ -868,75 +868,6 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "announcing an object" do
|
|
||||||
test "adds an announce activity to the db" do
|
|
||||||
note_activity = insert(:note_activity)
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
|
|
||||||
assert object.data["announcement_count"] == 1
|
|
||||||
assert object.data["announcements"] == [user.ap_id]
|
|
||||||
|
|
||||||
assert announce_activity.data["to"] == [
|
|
||||||
User.ap_followers(user),
|
|
||||||
note_activity.data["actor"]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert announce_activity.data["object"] == object.data["id"]
|
|
||||||
assert announce_activity.data["actor"] == user.ap_id
|
|
||||||
assert announce_activity.data["context"] == object.data["context"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "reverts annouce from object on error" do
|
|
||||||
note_activity = insert(:note_activity)
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
|
||||||
assert {:error, :reverted} = ActivityPub.announce(user, object)
|
|
||||||
end
|
|
||||||
|
|
||||||
reloaded_object = Object.get_by_ap_id(object.data["id"])
|
|
||||||
assert reloaded_object == object
|
|
||||||
refute reloaded_object.data["announcement_count"]
|
|
||||||
refute reloaded_object.data["announcements"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "announcing a private object" do
|
|
||||||
test "adds an announce activity to the db if the audience is not widened" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
|
|
||||||
{:ok, announce_activity, object} = ActivityPub.announce(user, object, nil, true, false)
|
|
||||||
|
|
||||||
assert announce_activity.data["to"] == [User.ap_followers(user)]
|
|
||||||
|
|
||||||
assert announce_activity.data["object"] == object.data["id"]
|
|
||||||
assert announce_activity.data["actor"] == user.ap_id
|
|
||||||
assert announce_activity.data["context"] == object.data["context"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not add an announce activity to the db if the audience is widened" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
|
|
||||||
assert {:error, _} = ActivityPub.announce(user, object, nil, true, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does not add an announce activity to the db if the announcer is not the author" do
|
|
||||||
user = insert(:user)
|
|
||||||
announcer = insert(:user)
|
|
||||||
{:ok, note_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
|
|
||||||
assert {:error, _} = ActivityPub.announce(announcer, object, nil, true, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "uploading files" do
|
describe "uploading files" do
|
||||||
test "copies the file to the configured folder" do
|
test "copies the file to the configured folder" do
|
||||||
file = %Plug.Upload{
|
file = %Plug.Upload{
|
||||||
|
|
|
@ -516,4 +516,96 @@ test "it works when actor or object are wrapped in maps", %{valid_like: valid_li
|
||||||
assert {:object, valid_like["object"]} in validated.changes
|
assert {:object, valid_like["object"]} in validated.changes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "announces" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
announcer = insert(:user)
|
||||||
|
{:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"})
|
||||||
|
|
||||||
|
object = Object.normalize(post_activity, false)
|
||||||
|
{:ok, valid_announce, []} = Builder.announce(announcer, object)
|
||||||
|
|
||||||
|
%{
|
||||||
|
valid_announce: valid_announce,
|
||||||
|
user: user,
|
||||||
|
post_activity: post_activity,
|
||||||
|
announcer: announcer
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns ok for a valid announce", %{valid_announce: valid_announce} do
|
||||||
|
assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if the object can't be found", %{valid_announce: valid_announce} do
|
||||||
|
without_object =
|
||||||
|
valid_announce
|
||||||
|
|> Map.delete("object")
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(without_object, [])
|
||||||
|
|
||||||
|
assert {:object, {"can't be blank", [validation: :required]}} in cng.errors
|
||||||
|
|
||||||
|
nonexisting_object =
|
||||||
|
valid_announce
|
||||||
|
|> Map.put("object", "https://gensokyo.2hu/objects/99999999")
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(nonexisting_object, [])
|
||||||
|
|
||||||
|
assert {:object, {"can't find object", []}} in cng.errors
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do
|
||||||
|
nonexisting_actor =
|
||||||
|
valid_announce
|
||||||
|
|> Map.put("actor", "https://gensokyo.2hu/users/raymoo")
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(nonexisting_actor, [])
|
||||||
|
|
||||||
|
assert {:actor, {"can't find user", []}} in cng.errors
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if the actor already announced the object", %{
|
||||||
|
valid_announce: valid_announce,
|
||||||
|
announcer: announcer,
|
||||||
|
post_activity: post_activity
|
||||||
|
} do
|
||||||
|
_announce = CommonAPI.repeat(post_activity.id, announcer)
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(valid_announce, [])
|
||||||
|
|
||||||
|
assert {:actor, {"already announced this object", []}} in cng.errors
|
||||||
|
assert {:object, {"already announced by this actor", []}} in cng.errors
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if the actor can't announce the object", %{
|
||||||
|
announcer: announcer,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
{:ok, post_activity} =
|
||||||
|
CommonAPI.post(user, %{status: "a secret post", visibility: "private"})
|
||||||
|
|
||||||
|
object = Object.normalize(post_activity, false)
|
||||||
|
|
||||||
|
# Another user can't announce it
|
||||||
|
{:ok, announce, []} = Builder.announce(announcer, object, public: false)
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
|
assert {:actor, {"can not announce this object", []}} in cng.errors
|
||||||
|
|
||||||
|
# The actor of the object can announce it
|
||||||
|
{:ok, announce, []} = Builder.announce(user, object, public: false)
|
||||||
|
|
||||||
|
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
|
# The actor of the object can not announce it publicly
|
||||||
|
{:ok, announce, []} = Builder.announce(user, object, public: true)
|
||||||
|
|
||||||
|
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||||
|
|
||||||
|
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,11 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "common_pipeline/2" do
|
describe "common_pipeline/2" do
|
||||||
|
setup do
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
|
test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
meta = [local: true]
|
meta = [local: true]
|
||||||
|
@ -83,5 +88,44 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
||||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
meta = [local: true]
|
||||||
|
|
||||||
|
with_mocks([
|
||||||
|
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
|
||||||
|
{
|
||||||
|
Pleroma.Web.ActivityPub.MRF,
|
||||||
|
[],
|
||||||
|
[filter: fn o -> {:ok, o} end]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub,
|
||||||
|
[],
|
||||||
|
[persist: fn o, m -> {:ok, o, m} end]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Pleroma.Web.ActivityPub.SideEffects,
|
||||||
|
[],
|
||||||
|
[handle: fn o, m -> {:ok, o, m} end]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Pleroma.Web.Federator,
|
||||||
|
[],
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
]) do
|
||||||
|
assert {:ok, ^activity, ^meta} =
|
||||||
|
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||||
|
|
||||||
|
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||||
|
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||||
|
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||||
|
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
@ -95,21 +94,20 @@ test "returns error when object is unknown" do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
assert Relay.publish(activity) == {:error, nil}
|
assert Relay.publish(activity) == {:error, false}
|
||||||
end) =~ "[error] error: nil"
|
end) =~ "[error] error: false"
|
||||||
end
|
end
|
||||||
|
|
||||||
test_with_mock "returns announce activity and publish to federate",
|
test_with_mock "returns announce activity and publish to federate",
|
||||||
Pleroma.Web.Federator,
|
Pleroma.Web.Federator,
|
||||||
[:passthrough],
|
[:passthrough],
|
||||||
[] do
|
[] do
|
||||||
Pleroma.Config.put([:instance, :federating], true)
|
clear_config([:instance, :federating], true)
|
||||||
service_actor = Relay.get_actor()
|
service_actor = Relay.get_actor()
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
|
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
||||||
assert activity.data["type"] == "Announce"
|
assert activity.data["type"] == "Announce"
|
||||||
assert activity.data["actor"] == service_actor.ap_id
|
assert activity.data["actor"] == service_actor.ap_id
|
||||||
assert activity.data["object"] == obj.data["id"]
|
|
||||||
assert called(Pleroma.Web.Federator.publish(activity))
|
assert called(Pleroma.Web.Federator.publish(activity))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -117,13 +115,12 @@ test "returns error when object is unknown" do
|
||||||
Pleroma.Web.Federator,
|
Pleroma.Web.Federator,
|
||||||
[:passthrough],
|
[:passthrough],
|
||||||
[] do
|
[] do
|
||||||
Pleroma.Config.put([:instance, :federating], false)
|
clear_config([:instance, :federating], false)
|
||||||
service_actor = Relay.get_actor()
|
service_actor = Relay.get_actor()
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)
|
assert {:ok, %Activity{} = activity} = Relay.publish(note)
|
||||||
assert activity.data["type"] == "Announce"
|
assert activity.data["type"] == "Announce"
|
||||||
assert activity.data["actor"] == service_actor.ap_id
|
assert activity.data["actor"] == service_actor.ap_id
|
||||||
assert activity.data["object"] == obj.data["id"]
|
|
||||||
refute called(Pleroma.Web.Federator.publish(activity))
|
refute called(Pleroma.Web.Federator.publish(activity))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -173,7 +173,7 @@ test "when activation is required", %{delete: delete, user: user} do
|
||||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||||
{:ok, like} = CommonAPI.favorite(user, post.id)
|
{:ok, like} = CommonAPI.favorite(user, post.id)
|
||||||
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
||||||
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
|
{:ok, announce} = CommonAPI.repeat(post.id, user)
|
||||||
{:ok, block} = ActivityPub.block(user, poster)
|
{:ok, block} = ActivityPub.block(user, poster)
|
||||||
User.block(user, poster)
|
User.block(user, poster)
|
||||||
|
|
||||||
|
@ -376,4 +376,61 @@ test "it creates a Chat for the local users and bumps the unread count" do
|
||||||
assert chat
|
assert chat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "announce objects" do
|
||||||
|
setup do
|
||||||
|
poster = insert(:user)
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||||
|
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
||||||
|
|
||||||
|
{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
|
||||||
|
|
||||||
|
{:ok, private_announce_data, _meta} =
|
||||||
|
Builder.announce(user, private_post.object, public: false)
|
||||||
|
|
||||||
|
{:ok, relay_announce_data, _meta} =
|
||||||
|
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
|
||||||
|
|
||||||
|
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
||||||
|
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
||||||
|
{:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
|
||||||
|
|
||||||
|
%{
|
||||||
|
announce: announce,
|
||||||
|
user: user,
|
||||||
|
poster: poster,
|
||||||
|
private_announce: private_announce,
|
||||||
|
relay_announce: relay_announce
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "adds the announce to the original object", %{announce: announce, user: user} do
|
||||||
|
{:ok, announce, _} = SideEffects.handle(announce)
|
||||||
|
object = Object.get_by_ap_id(announce.data["object"])
|
||||||
|
assert object.data["announcement_count"] == 1
|
||||||
|
assert user.ap_id in object.data["announcements"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not add the announce to the original object if the actor is a service actor", %{
|
||||||
|
relay_announce: announce
|
||||||
|
} do
|
||||||
|
{:ok, announce, _} = SideEffects.handle(announce)
|
||||||
|
object = Object.get_by_ap_id(announce.data["object"])
|
||||||
|
assert object.data["announcement_count"] == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates a notification", %{announce: announce, poster: poster} do
|
||||||
|
{:ok, announce, _} = SideEffects.handle(announce)
|
||||||
|
assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it streams out the announce", %{announce: announce} do
|
||||||
|
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
|
||||||
|
{:ok, announce, _} = SideEffects.handle(announce)
|
||||||
|
|
||||||
|
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
172
test/web/activity_pub/transmogrifier/announce_handling_test.exs
Normal file
172
test/web/activity_pub/transmogrifier/announce_handling_test.exs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
test "it works for incoming honk announces" do
|
||||||
|
user = insert(:user, ap_id: "https://honktest/u/test", local: false)
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
|
||||||
|
|
||||||
|
announce = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor" => "https://honktest/u/test",
|
||||||
|
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
|
||||||
|
"object" => post.data["object"],
|
||||||
|
"published" => "2019-06-25T19:33:58Z",
|
||||||
|
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"type" => "Announce"
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)
|
||||||
|
|
||||||
|
object = Object.get_by_ap_id(post.data["object"])
|
||||||
|
|
||||||
|
assert length(object.data["announcements"]) == 1
|
||||||
|
assert user.ap_id in object.data["announcements"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for incoming announces with actor being inlined (kroeg)" do
|
||||||
|
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
_user = insert(:user, local: false, ap_id: data["actor"]["id"])
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> put_in(["object", "id"], post.data["object"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "https://puckipedia.com/"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for incoming announces, fetching the announced object" do
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367")
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{method: :get} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
assert data["type"] == "Announce"
|
||||||
|
|
||||||
|
assert data["id"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||||
|
|
||||||
|
assert data["object"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
||||||
|
|
||||||
|
assert(Activity.get_create_by_object_ap_id(data["object"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
|
test "it works for incoming announces with an existing activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"])
|
||||||
|
|
||||||
|
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
assert data["type"] == "Announce"
|
||||||
|
|
||||||
|
assert data["id"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||||
|
|
||||||
|
assert data["object"] == activity.data["object"]
|
||||||
|
|
||||||
|
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ignore inlined activities for now
|
||||||
|
@tag skip: true
|
||||||
|
test "it works for incoming announces with an inlined activity" do
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce-private.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
_user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
ap_id: data["actor"],
|
||||||
|
follower_address: data["actor"] <> "/followers"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
assert data["type"] == "Announce"
|
||||||
|
|
||||||
|
assert data["id"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||||
|
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
|
assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
|
||||||
|
assert object.data["content"] == "this is a private toot"
|
||||||
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
|
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{method: :get} -> %Tesla.Env{status: 404, body: ""}
|
||||||
|
end)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|
||||||
|
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||||
|
|
||||||
|
assert {:error, e} = Transmogrifier.handle_incoming(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it does not clobber the addressing on announce activities" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", Object.normalize(activity).data["id"])
|
||||||
|
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
_user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
ap_id: data["actor"],
|
||||||
|
follower_address: "http://mastodon.example.org/users/admin/followers"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
|
||||||
|
end
|
||||||
|
end
|
|
@ -28,6 +28,63 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
setup do: clear_config([:instance, :max_remote_account_fields])
|
setup do: clear_config([:instance, :max_remote_account_fields])
|
||||||
|
|
||||||
describe "handle_incoming" do
|
describe "handle_incoming" do
|
||||||
|
test "it works for incoming notices with tag not being an array (kroeg)" do
|
||||||
|
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
|
assert object.data["emoji"] == %{
|
||||||
|
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
|
assert "test" in object.data["tag"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for incoming notices with url not being a string (prismo)" do
|
||||||
|
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
object = Object.normalize(data["object"])
|
||||||
|
|
||||||
|
assert object.data["url"] == "https://prismo.news/posts/83"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it cleans up incoming notices which are not really DMs" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
to = [user.ap_id, other_user.ap_id]
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("to", to)
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
object =
|
||||||
|
data["object"]
|
||||||
|
|> Map.put("to", to)
|
||||||
|
|> Map.put("cc", [])
|
||||||
|
|
||||||
|
data = Map.put(data, "object", object)
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["to"] == []
|
||||||
|
assert data["cc"] == to
|
||||||
|
|
||||||
|
object_data = Object.normalize(activity).data
|
||||||
|
|
||||||
|
assert object_data["to"] == []
|
||||||
|
assert object_data["cc"] == to
|
||||||
|
end
|
||||||
|
|
||||||
test "it ignores an incoming notice if we already have it" do
|
test "it ignores an incoming notice if we already have it" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
@ -260,172 +317,6 @@ test "it works for incoming notices with to/cc not being an array (kroeg)" do
|
||||||
"<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
|
"<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it works for incoming honk announces" do
|
|
||||||
_user = insert(:user, ap_id: "https://honktest/u/test", local: false)
|
|
||||||
other_user = insert(:user)
|
|
||||||
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
|
|
||||||
|
|
||||||
announce = %{
|
|
||||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
|
||||||
"actor" => "https://honktest/u/test",
|
|
||||||
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
|
|
||||||
"object" => post.data["object"],
|
|
||||||
"published" => "2019-06-25T19:33:58Z",
|
|
||||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"type" => "Announce"
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming announces with actor being inlined (kroeg)" do
|
|
||||||
data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["actor"] == "https://puckipedia.com/"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming notices with tag not being an array (kroeg)" do
|
|
||||||
data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
object = Object.normalize(data["object"])
|
|
||||||
|
|
||||||
assert object.data["emoji"] == %{
|
|
||||||
"icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
object = Object.normalize(data["object"])
|
|
||||||
|
|
||||||
assert "test" in object.data["tag"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming notices with url not being a string (prismo)" do
|
|
||||||
data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
object = Object.normalize(data["object"])
|
|
||||||
|
|
||||||
assert object.data["url"] == "https://prismo.news/posts/83"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it cleans up incoming notices which are not really DMs" do
|
|
||||||
user = insert(:user)
|
|
||||||
other_user = insert(:user)
|
|
||||||
|
|
||||||
to = [user.ap_id, other_user.ap_id]
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("to", to)
|
|
||||||
|> Map.put("cc", [])
|
|
||||||
|
|
||||||
object =
|
|
||||||
data["object"]
|
|
||||||
|> Map.put("to", to)
|
|
||||||
|> Map.put("cc", [])
|
|
||||||
|
|
||||||
data = Map.put(data, "object", object)
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["to"] == []
|
|
||||||
assert data["cc"] == to
|
|
||||||
|
|
||||||
object_data = Object.normalize(activity).data
|
|
||||||
|
|
||||||
assert object_data["to"] == []
|
|
||||||
assert object_data["cc"] == to
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming announces" do
|
|
||||||
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
|
||||||
assert data["type"] == "Announce"
|
|
||||||
|
|
||||||
assert data["id"] ==
|
|
||||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
|
||||||
|
|
||||||
assert data["object"] ==
|
|
||||||
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
|
|
||||||
|
|
||||||
assert Activity.get_create_by_object_ap_id(data["object"])
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming announces with an existing activity" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/mastodon-announce.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("object", activity.data["object"])
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
|
||||||
assert data["type"] == "Announce"
|
|
||||||
|
|
||||||
assert data["id"] ==
|
|
||||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
|
||||||
|
|
||||||
assert data["object"] == activity.data["object"]
|
|
||||||
|
|
||||||
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it works for incoming announces with an inlined activity" do
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/mastodon-announce-private.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
|
||||||
assert data["type"] == "Announce"
|
|
||||||
|
|
||||||
assert data["id"] ==
|
|
||||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
|
||||||
|
|
||||||
object = Object.normalize(data["object"])
|
|
||||||
|
|
||||||
assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368"
|
|
||||||
assert object.data["content"] == "this is a private toot"
|
|
||||||
end
|
|
||||||
|
|
||||||
@tag capture_log: true
|
|
||||||
test "it rejects incoming announces with an inlined activity from another origin" do
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|
|
||||||
assert :error = Transmogrifier.handle_incoming(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it does not clobber the addressing on announce activities" do
|
|
||||||
user = insert(:user)
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
|
||||||
|
|
||||||
data =
|
|
||||||
File.read!("test/fixtures/mastodon-announce.json")
|
|
||||||
|> Poison.decode!()
|
|
||||||
|> Map.put("object", Object.normalize(activity).data["id"])
|
|
||||||
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|
|
||||||
|> Map.put("cc", [])
|
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
|
||||||
|
|
||||||
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it ensures that as:Public activities make it to their followers collection" do
|
test "it ensures that as:Public activities make it to their followers collection" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -1188,7 +1079,7 @@ test "it inlines private announced objects" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
|
||||||
|
|
||||||
{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
{:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
|
{:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
|
||||||
|
|
||||||
|
@ -1438,7 +1329,7 @@ test "it rejects activities which reference objects with bogus origins" do
|
||||||
}
|
}
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
:error = Transmogrifier.handle_incoming(data)
|
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||||
end) =~ "Object containment failed"
|
end) =~ "Object containment failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1453,7 +1344,7 @@ test "it rejects activities which reference objects that have an incorrect attri
|
||||||
}
|
}
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
:error = Transmogrifier.handle_incoming(data)
|
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||||
end) =~ "Object containment failed"
|
end) =~ "Object containment failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1468,7 +1359,7 @@ test "it rejects activities which reference objects that have an incorrect attri
|
||||||
}
|
}
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
:error = Transmogrifier.handle_incoming(data)
|
{:error, _} = Transmogrifier.handle_incoming(data)
|
||||||
end) =~ "Object containment failed"
|
end) =~ "Object containment failed"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -334,7 +334,7 @@ test "fetches existing announce" do
|
||||||
assert object = Object.normalize(note_activity)
|
assert object = Object.normalize(note_activity)
|
||||||
actor = insert(:user)
|
actor = insert(:user)
|
||||||
|
|
||||||
{:ok, announce, _object} = ActivityPub.announce(actor, object)
|
{:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
|
||||||
assert Utils.get_existing_announce(actor.ap_id, object) == announce
|
assert Utils.get_existing_announce(actor.ap_id, object) == announce
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,7 +73,7 @@ test "renders an announce activity" do
|
||||||
object = Object.normalize(note)
|
object = Object.normalize(note)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
{:ok, announce_activity, _} = CommonAPI.repeat(note.id, user)
|
{:ok, announce_activity} = CommonAPI.repeat(note.id, user)
|
||||||
|
|
||||||
result = ObjectView.render("object.json", %{object: announce_activity})
|
result = ObjectView.render("object.json", %{object: announce_activity})
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ test "GET /api/pleroma/admin/users/:nickname requires " <>
|
||||||
describe "DELETE /api/pleroma/admin/users" do
|
describe "DELETE /api/pleroma/admin/users" do
|
||||||
test "single user", %{admin: admin, conn: conn} do
|
test "single user", %{admin: admin, conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
with_mock Pleroma.Web.Federator,
|
with_mock Pleroma.Web.Federator,
|
||||||
publish: fn _ -> nil end do
|
publish: fn _ -> nil end do
|
||||||
|
@ -350,7 +351,7 @@ test "when the user doesn't exist", %{conn: conn} do
|
||||||
|
|
||||||
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
|
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
|
||||||
|
|
||||||
assert "Not found" == json_response(conn, 404)
|
assert %{"error" => "Not found"} == json_response(conn, 404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -683,7 +684,10 @@ test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
|
||||||
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
||||||
|
|
||||||
assert json_response(conn, :bad_request) ==
|
assert json_response(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
"To send invites you need to set the `invites_enabled` option to true."
|
"To send invites you need to set the `invites_enabled` option to true."
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
||||||
|
@ -693,7 +697,10 @@ test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
|
||||||
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
|
||||||
|
|
||||||
assert json_response(conn, :bad_request) ==
|
assert json_response(conn, :bad_request) ==
|
||||||
|
%{
|
||||||
|
"error" =>
|
||||||
"To send invites you need to set the `registrations_open` option to false."
|
"To send invites you need to set the `registrations_open` option to false."
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1307,7 +1314,7 @@ test "returns 404 if user not found", %{conn: conn} do
|
||||||
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
|
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
|
||||||
|> json_response(404)
|
|> json_response(404)
|
||||||
|
|
||||||
assert response == "Not found"
|
assert response == %{"error" => "Not found"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1413,7 +1420,7 @@ test "with token", %{conn: conn} do
|
||||||
test "with invalid token", %{conn: conn} do
|
test "with invalid token", %{conn: conn} do
|
||||||
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
|
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
|
||||||
|
|
||||||
assert json_response(conn, :not_found) == "Not found"
|
assert json_response(conn, :not_found) == %{"error" => "Not found"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1440,7 +1447,7 @@ test "returns report by its id", %{conn: conn} do
|
||||||
test "returns 404 when report id is invalid", %{conn: conn} do
|
test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
conn = get(conn, "/api/pleroma/admin/reports/test")
|
conn = get(conn, "/api/pleroma/admin/reports/test")
|
||||||
|
|
||||||
assert json_response(conn, :not_found) == "Not found"
|
assert json_response(conn, :not_found) == %{"error" => "Not found"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1697,115 +1704,6 @@ test "returns 403 when requested by anonymous" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/statuses/:id" do
|
|
||||||
test "not found", %{conn: conn} do
|
|
||||||
assert conn
|
|
||||||
|> get("/api/pleroma/admin/statuses/not_found")
|
|
||||||
|> json_response(:not_found)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "shows activity", %{conn: conn} do
|
|
||||||
activity = insert(:note_activity)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/statuses/#{activity.id}")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert response["id"] == activity.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PUT /api/pleroma/admin/statuses/:id" do
|
|
||||||
setup do
|
|
||||||
activity = insert(:note_activity)
|
|
||||||
|
|
||||||
%{id: activity.id}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response["sensitive"]
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
refute response["sensitive"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "change visibility flag", %{conn: conn, id: id, admin: admin} do
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response["visibility"] == "public"
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response["visibility"] == "private"
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"})
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
assert response["visibility"] == "unlisted"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
|
|
||||||
conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{visibility: "test"})
|
|
||||||
|
|
||||||
assert json_response(conn, :bad_request) == "Unsupported visibility"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "DELETE /api/pleroma/admin/statuses/:id" do
|
|
||||||
setup do
|
|
||||||
activity = insert(:note_activity)
|
|
||||||
|
|
||||||
%{id: activity.id}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "deletes status", %{conn: conn, id: id, admin: admin} do
|
|
||||||
conn
|
|
||||||
|> delete("/api/pleroma/admin/statuses/#{id}")
|
|
||||||
|> json_response(:ok)
|
|
||||||
|
|
||||||
refute Activity.get_by_id(id)
|
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
|
||||||
"@#{admin.nickname} deleted status ##{id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 404 when the status does not exist", %{conn: conn} do
|
|
||||||
conn = delete(conn, "/api/pleroma/admin/statuses/test")
|
|
||||||
|
|
||||||
assert json_response(conn, :not_found) == "Not found"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/config" do
|
describe "GET /api/pleroma/admin/config" do
|
||||||
setup do: clear_config(:configurable_from_database, true)
|
setup do: clear_config(:configurable_from_database, true)
|
||||||
|
|
||||||
|
@ -1814,7 +1712,9 @@ test "when configuration from database is off", %{conn: conn} do
|
||||||
conn = get(conn, "/api/pleroma/admin/config")
|
conn = get(conn, "/api/pleroma/admin/config")
|
||||||
|
|
||||||
assert json_response(conn, 400) ==
|
assert json_response(conn, 400) ==
|
||||||
"To use this endpoint you need to enable configuration from database."
|
%{
|
||||||
|
"error" => "To use this endpoint you need to enable configuration from database."
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with settings only in db", %{conn: conn} do
|
test "with settings only in db", %{conn: conn} do
|
||||||
|
@ -1936,7 +1836,7 @@ test "POST /api/pleroma/admin/config error", %{conn: conn} do
|
||||||
conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
|
conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
|
||||||
|
|
||||||
assert json_response(conn, 400) ==
|
assert json_response(conn, 400) ==
|
||||||
"To use this endpoint you need to enable configuration from database."
|
%{"error" => "To use this endpoint you need to enable configuration from database."}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /api/pleroma/admin/config" do
|
describe "POST /api/pleroma/admin/config" do
|
||||||
|
@ -2944,6 +2844,7 @@ test "proxy tuple ip", %{conn: conn} do
|
||||||
assert ":proxy_url" in db
|
assert ":proxy_url" in db
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "doesn't set keys not in the whitelist", %{conn: conn} do
|
test "doesn't set keys not in the whitelist", %{conn: conn} do
|
||||||
clear_config(:database_config_whitelist, [
|
clear_config(:database_config_whitelist, [
|
||||||
{:pleroma, :key1},
|
{:pleroma, :key1},
|
||||||
|
@ -2998,54 +2899,6 @@ test "need_reboot flag", %{conn: conn} do
|
||||||
on_exit(fn -> Restarter.Pleroma.refresh() end)
|
on_exit(fn -> Restarter.Pleroma.refresh() end)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/statuses" do
|
|
||||||
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
|
|
||||||
blocked = insert(:user)
|
|
||||||
user = insert(:user)
|
|
||||||
User.block(admin, blocked)
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
|
|
||||||
{:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"})
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/statuses")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
refute "private" in Enum.map(response, & &1["visibility"])
|
|
||||||
assert length(response) == 3
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns only local statuses with local_only on", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
|
||||||
insert(:note_activity, user: user, local: true)
|
|
||||||
insert(:note_activity, user: remote_user, local: false)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> get("/api/pleroma/admin/statuses?local_only=true")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert length(response) == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
|
||||||
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
|
|
||||||
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
|
|
||||||
assert json_response(conn, 200) |> length() == 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/users/:nickname/statuses" do
|
describe "GET /api/pleroma/admin/users/:nickname/statuses" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -3096,7 +2949,7 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do
|
||||||
test "excludes reblogs by default", %{conn: conn, user: user} do
|
test "excludes reblogs by default", %{conn: conn, user: user} do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "."})
|
{:ok, activity} = CommonAPI.post(user, %{status: "."})
|
||||||
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
|
conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
|
||||||
assert json_response(conn_res, 200) |> length() == 0
|
assert json_response(conn_res, 200) |> length() == 0
|
194
test/web/admin_api/controllers/status_controller_test.exs
Normal file
194
test/web/admin_api/controllers/status_controller_test.exs
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/statuses/:id" do
|
||||||
|
test "not found", %{conn: conn} do
|
||||||
|
assert conn
|
||||||
|
|> get("/api/pleroma/admin/statuses/not_found")
|
||||||
|
|> json_response_and_validate_schema(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "shows activity", %{conn: conn} do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/statuses/#{activity.id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["id"] == activity.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT /api/pleroma/admin/statuses/:id" do
|
||||||
|
setup do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
%{id: activity.id}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["sensitive"]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
refute response["sensitive"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "change visibility flag", %{conn: conn, id: id, admin: admin} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["visibility"] == "public"
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["visibility"] == "private"
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response["visibility"] == "unlisted"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "test"})
|
||||||
|
|
||||||
|
assert %{"error" => "test - Invalid value for enum."} =
|
||||||
|
json_response_and_validate_schema(conn, :bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/pleroma/admin/statuses/:id" do
|
||||||
|
setup do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
%{id: activity.id}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "deletes status", %{conn: conn, id: id, admin: admin} do
|
||||||
|
conn
|
||||||
|
|> delete("/api/pleroma/admin/statuses/#{id}")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
refute Activity.get_by_id(id)
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deleted status ##{id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when the status does not exist", %{conn: conn} do
|
||||||
|
conn = delete(conn, "/api/pleroma/admin/statuses/test")
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/statuses" do
|
||||||
|
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
|
||||||
|
blocked = insert(:user)
|
||||||
|
user = insert(:user)
|
||||||
|
User.block(admin, blocked)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
|
||||||
|
{:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/statuses")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
refute "private" in Enum.map(response, & &1["visibility"])
|
||||||
|
assert length(response) == 3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns only local statuses with local_only on", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
||||||
|
insert(:note_activity, user: user, local: true)
|
||||||
|
insert(:note_activity, user: remote_user, local: false)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/statuses?local_only=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
|
||||||
|
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
|
||||||
|
assert json_response_and_validate_schema(conn, 200) |> length() == 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -116,6 +116,8 @@ test "it works with pruned objects" do
|
||||||
|
|
||||||
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
||||||
|
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
Object.normalize(post, false)
|
Object.normalize(post, false)
|
||||||
|> Object.prune()
|
|> Object.prune()
|
||||||
|
|
||||||
|
@ -134,6 +136,8 @@ test "it allows users to delete their posts" do
|
||||||
|
|
||||||
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
|
||||||
|
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
with_mock Pleroma.Web.Federator,
|
with_mock Pleroma.Web.Federator,
|
||||||
publish: fn _ -> nil end do
|
publish: fn _ -> nil end do
|
||||||
assert {:ok, delete} = CommonAPI.delete(post.id, user)
|
assert {:ok, delete} = CommonAPI.delete(post.id, user)
|
||||||
|
@ -410,6 +414,32 @@ test "it does not allow replies to direct messages that are not direct messages
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
third_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
|
||||||
|
|
||||||
|
{:ok, open_answer} =
|
||||||
|
CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
|
||||||
|
|
||||||
|
# The OP is implicitly added
|
||||||
|
assert user.ap_id in open_answer.recipients
|
||||||
|
|
||||||
|
{:ok, secret_answer} =
|
||||||
|
CommonAPI.post(other_user, %{
|
||||||
|
status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
|
||||||
|
in_reply_to_status_id: post.id,
|
||||||
|
visibility: "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert third_user.ap_id in secret_answer.recipients
|
||||||
|
|
||||||
|
# The OP is not added
|
||||||
|
refute user.ap_id in secret_answer.recipients
|
||||||
|
end
|
||||||
|
|
||||||
test "it allows to address a list" do
|
test "it allows to address a list" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("foo", user)
|
{:ok, list} = Pleroma.List.create("foo", user)
|
||||||
|
@ -491,7 +521,8 @@ test "repeating a status" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||||
|
|
||||||
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||||
|
assert Visibility.is_public?(announce_activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "can't repeat a repeat" do
|
test "can't repeat a repeat" do
|
||||||
|
@ -499,9 +530,9 @@ test "can't repeat a repeat" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||||
|
|
||||||
{:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user))
|
refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "repeating a status privately" do
|
test "repeating a status privately" do
|
||||||
|
@ -510,10 +541,11 @@ test "repeating a status privately" do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||||
|
|
||||||
{:ok, %Activity{} = announce_activity, _} =
|
{:ok, %Activity{} = announce_activity} =
|
||||||
CommonAPI.repeat(activity.id, user, %{visibility: "private"})
|
CommonAPI.repeat(activity.id, user, %{visibility: "private"})
|
||||||
|
|
||||||
assert Visibility.is_private?(announce_activity)
|
assert Visibility.is_private?(announce_activity)
|
||||||
|
refute Visibility.visible_for_user?(announce_activity, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "favoriting a status" do
|
test "favoriting a status" do
|
||||||
|
@ -533,8 +565,8 @@ test "retweeting a status twice returns the status" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||||
{:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
|
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
|
||||||
{:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
|
{:ok, ^announce} = CommonAPI.repeat(activity.id, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "favoriting a status twice returns ok, but without the like activity" do
|
test "favoriting a status twice returns ok, but without the like activity" do
|
||||||
|
|
|
@ -14,18 +14,41 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
|
|
||||||
@public_address "https://www.w3.org/ns/activitystreams#Public"
|
@public_address "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
test "it adds attachment links to a given text and attachment set" do
|
describe "add_attachments/2" do
|
||||||
|
setup do
|
||||||
name =
|
name =
|
||||||
"Sakura%20Mana%20%E2%80%93%20Turned%20on%20by%20a%20Senior%20OL%20with%20a%20Temptating%20Tight%20Skirt-s%20Full%20Hipline%20and%20Panty%20Shot-%20Beautiful%20Thick%20Thighs-%20and%20Erotic%20Ass-%20-2015-%20--%20Oppaitime%208-28-2017%206-50-33%20PM.png"
|
"Sakura Mana – Turned on by a Senior OL with a Temptating Tight Skirt-s Full Hipline and Panty Shot- Beautiful Thick Thighs- and Erotic Ass- -2015- -- Oppaitime 8-28-2017 6-50-33 PM.png"
|
||||||
|
|
||||||
attachment = %{
|
attachment = %{
|
||||||
"url" => [%{"href" => name}]
|
"url" => [%{"href" => URI.encode(name)}]
|
||||||
}
|
}
|
||||||
|
|
||||||
res = Utils.add_attachments("", [attachment])
|
%{name: name, attachment: attachment}
|
||||||
|
end
|
||||||
|
|
||||||
assert res ==
|
test "it adds attachment links to a given text and attachment set", %{
|
||||||
"<br><a href=\"#{name}\" class='attachment'>Sakura Mana – Turned on by a Se…</a>"
|
name: name,
|
||||||
|
attachment: attachment
|
||||||
|
} do
|
||||||
|
len = 10
|
||||||
|
clear_config([Pleroma.Upload, :filename_display_max_length], len)
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{String.slice(name, 0..len)}…</a>"
|
||||||
|
|
||||||
|
assert Utils.add_attachments("", [attachment]) == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't truncate file name if config for truncate is set to 0", %{
|
||||||
|
name: name,
|
||||||
|
attachment: attachment
|
||||||
|
} do
|
||||||
|
clear_config([Pleroma.Upload, :filename_display_max_length], 0)
|
||||||
|
|
||||||
|
expected = "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{name}</a>"
|
||||||
|
|
||||||
|
assert Utils.add_attachments("", [attachment]) == expected
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "it confirms the password given is the current users password" do
|
describe "it confirms the password given is the current users password" do
|
||||||
|
@ -297,11 +320,10 @@ test "for private posts, a reply" do
|
||||||
|
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)
|
||||||
|
|
||||||
assert length(to) == 3
|
assert length(to) == 2
|
||||||
assert Enum.empty?(cc)
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
assert mentioned_user.ap_id in to
|
assert mentioned_user.ap_id in to
|
||||||
assert third_user.ap_id in to
|
|
||||||
assert user.follower_address in to
|
assert user.follower_address in to
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -327,6 +349,15 @@ test "for direct posts, a reply" do
|
||||||
|
|
||||||
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)
|
{to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)
|
||||||
|
|
||||||
|
assert length(to) == 1
|
||||||
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
|
assert mentioned_user.ap_id in to
|
||||||
|
|
||||||
|
{:ok, direct_activity} = CommonAPI.post(third_user, %{status: "uguu", visibility: "direct"})
|
||||||
|
|
||||||
|
{to, cc} = Utils.get_to_and_cc(user, mentions, direct_activity, "direct", nil)
|
||||||
|
|
||||||
assert length(to) == 2
|
assert length(to) == 2
|
||||||
assert Enum.empty?(cc)
|
assert Enum.empty?(cc)
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,7 @@ test "respects blocks", %{user: user_one, conn: conn} do
|
||||||
User.block(user_one, user_two)
|
User.block(user_one, user_two)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
|
{:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
|
||||||
{:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
|
{:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
|
||||||
|
|
||||||
assert resp =
|
assert resp =
|
||||||
conn
|
conn
|
||||||
|
@ -375,7 +375,7 @@ test "gets an users media", %{conn: conn} do
|
||||||
|
|
||||||
test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
|
test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
|
||||||
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
|
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
|
||||||
{:ok, _, _} = CommonAPI.repeat(post_id, user)
|
{:ok, _} = CommonAPI.repeat(post_id, user)
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
|
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
|
||||||
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
|
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
|
||||||
|
@ -678,7 +678,7 @@ test "following without reblogs" do
|
||||||
assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
|
assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
||||||
{:ok, %{id: reblog_id}, _} = CommonAPI.repeat(activity.id, followed)
|
{:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
|
||||||
|
|
||||||
assert [] ==
|
assert [] ==
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -280,8 +280,8 @@ test "filters notifications for Announce activities" do
|
||||||
{:ok, unlisted_activity} =
|
{:ok, unlisted_activity} =
|
||||||
CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"})
|
CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"})
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
|
{:ok, _} = CommonAPI.repeat(public_activity.id, user)
|
||||||
{:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
|
{:ok, _} = CommonAPI.repeat(unlisted_activity.id, user)
|
||||||
|
|
||||||
activity_ids =
|
activity_ids =
|
||||||
conn
|
conn
|
||||||
|
@ -301,7 +301,7 @@ test "filters notifications using exclude_types" do
|
||||||
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
||||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
||||||
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
|
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
|
||||||
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
||||||
|
|
||||||
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
||||||
|
@ -339,7 +339,7 @@ test "filters notifications using include_types" do
|
||||||
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
|
||||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
|
||||||
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
|
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user)
|
||||||
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
|
||||||
|
|
||||||
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
mention_notification_id = get_notification_id_by_activity(mention_activity)
|
||||||
|
|
|
@ -878,8 +878,8 @@ test "reblogged status for another user" do
|
||||||
user3 = insert(:user)
|
user3 = insert(:user)
|
||||||
{:ok, _} = CommonAPI.favorite(user2, activity.id)
|
{:ok, _} = CommonAPI.favorite(user2, activity.id)
|
||||||
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
|
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
|
||||||
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
|
{:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
|
||||||
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
|
{:ok, _} = CommonAPI.repeat(activity.id, user2)
|
||||||
|
|
||||||
conn_res =
|
conn_res =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -917,7 +917,7 @@ test "reblogged status for another user" do
|
||||||
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
|
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, _} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -1427,7 +1427,7 @@ test "requires authentication for private posts", %{user: user} do
|
||||||
|
|
||||||
test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
|
test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|
@ -1458,7 +1458,7 @@ test "does not return users who have reblogged the status but are blocked", %{
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, _user_relationship} = User.block(user, other_user)
|
{:ok, _user_relationship} = User.block(user, other_user)
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|
@ -1469,12 +1469,12 @@ test "does not return users who have reblogged the status but are blocked", %{
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return users who have reblogged the status privately", %{
|
test "does not return users who have reblogged the status privately", %{
|
||||||
conn: conn,
|
conn: conn
|
||||||
activity: activity
|
|
||||||
} do
|
} do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
|
||||||
|
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|
@ -1486,7 +1486,7 @@ test "does not return users who have reblogged the status privately", %{
|
||||||
|
|
||||||
test "does not fail on an unauthenticated request", %{activity: activity} do
|
test "does not fail on an unauthenticated request", %{activity: activity} do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
response =
|
response =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
|
|
@ -104,7 +104,7 @@ test "Reblog notification" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
another_user = insert(:user)
|
another_user = insert(:user)
|
||||||
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
{:ok, reblog_activity, _object} = CommonAPI.repeat(create_activity.id, another_user)
|
{:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, another_user)
|
||||||
{:ok, [notification]} = Notification.create_notifications(reblog_activity)
|
{:ok, [notification]} = Notification.create_notifications(reblog_activity)
|
||||||
reblog_activity = Activity.get_by_id(create_activity.id)
|
reblog_activity = Activity.get_by_id(create_activity.id)
|
||||||
|
|
||||||
|
|
|
@ -442,7 +442,7 @@ test "a reblog" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
{:ok, reblog, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, reblog} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
represented = StatusView.render("show.json", %{for: user, activity: reblog})
|
represented = StatusView.render("show.json", %{for: user, activity: reblog})
|
||||||
|
|
||||||
|
@ -600,7 +600,7 @@ test "does not embed a relationship in the account in reposts" do
|
||||||
status: "˙˙ɐʎns"
|
status: "˙˙ɐʎns"
|
||||||
})
|
})
|
||||||
|
|
||||||
{:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
|
{:ok, activity} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
result = StatusView.render("show.json", %{activity: activity, for: user})
|
result = StatusView.render("show.json", %{activity: activity, for: user})
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,11 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
setup_all do
|
setup_all do
|
||||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
@ -19,6 +23,47 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
|
|
||||||
setup do: clear_config([:instance, :federating], true)
|
setup do: clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
|
describe "Mastodon compatibility routes" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
conn = put_req_header(conn, "accept", "text/html")
|
||||||
|
|
||||||
|
{:ok, object} =
|
||||||
|
%{
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "hey",
|
||||||
|
"id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
|
||||||
|
"actor" => Endpoint.url() <> "/users/raymoo",
|
||||||
|
"to" => [Pleroma.Constants.as_public()]
|
||||||
|
}
|
||||||
|
|> Object.create()
|
||||||
|
|
||||||
|
{:ok, activity, _} =
|
||||||
|
%{
|
||||||
|
"id" => object.data["id"] <> "/activity",
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => object.data["id"],
|
||||||
|
"actor" => object.data["actor"],
|
||||||
|
"to" => object.data["to"]
|
||||||
|
}
|
||||||
|
|> ActivityPub.persist(local: true)
|
||||||
|
|
||||||
|
%{conn: conn, activity: activity}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do
|
||||||
|
conn = get(conn, "/users/raymoo/statuses/999999999")
|
||||||
|
assert redirected_to(conn) == "/notice/#{activity.id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "redirects to /notice/:id for html format for activity", %{
|
||||||
|
conn: conn,
|
||||||
|
activity: activity
|
||||||
|
} do
|
||||||
|
conn = get(conn, "/users/raymoo/statuses/999999999/activity")
|
||||||
|
assert redirected_to(conn) == "/notice/#{activity.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Note: see ActivityPubControllerTest for JSON format tests
|
# Note: see ActivityPubControllerTest for JSON format tests
|
||||||
describe "GET /objects/:uuid (text/html)" do
|
describe "GET /objects/:uuid (text/html)" do
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
|
|
|
@ -33,6 +33,13 @@ test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
||||||
assert result["pleroma"]["emoji_reactions"] == [
|
assert result["pleroma"]["emoji_reactions"] == [
|
||||||
%{"name" => "☕", "count" => 1, "me" => true}
|
%{"name" => "☕", "count" => 1, "me" => true}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Reacting with a non-emoji
|
||||||
|
assert conn
|
||||||
|
|> assign(:user, other_user)
|
||||||
|
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|
||||||
|
|> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x")
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
|
||||||
|
|
|
@ -23,7 +23,8 @@ test "it marks a single notification as read", %{user: user1, conn: conn} do
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> post("/api/v1/pleroma/notifications/read?id=#{notification1.id}")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{id: notification1.id})
|
||||||
|> json_response_and_validate_schema(:ok)
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
assert %{"pleroma" => %{"is_seen" => true}} = response
|
assert %{"pleroma" => %{"is_seen" => true}} = response
|
||||||
|
@ -41,7 +42,8 @@ test "it marks multiple notifications as read", %{user: user1, conn: conn} do
|
||||||
|
|
||||||
[response1, response2] =
|
[response1, response2] =
|
||||||
conn
|
conn
|
||||||
|> post("/api/v1/pleroma/notifications/read?max_id=#{notification2.id}")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id})
|
||||||
|> json_response_and_validate_schema(:ok)
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
assert %{"pleroma" => %{"is_seen" => true}} = response1
|
assert %{"pleroma" => %{"is_seen" => true}} = response1
|
||||||
|
@ -54,7 +56,10 @@ test "it marks multiple notifications as read", %{user: user1, conn: conn} do
|
||||||
test "it returns error when notification not found", %{conn: conn} do
|
test "it returns error when notification not found", %{conn: conn} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> post("/api/v1/pleroma/notifications/read?id=22222222222222")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/pleroma/notifications/read", %{
|
||||||
|
id: 22_222_222_222_222
|
||||||
|
})
|
||||||
|> json_response_and_validate_schema(:bad_request)
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
|
||||||
assert response == %{"error" => "Cannot get notification"}
|
assert response == %{"error" => "Cannot get notification"}
|
||||||
|
|
|
@ -151,7 +151,7 @@ test "renders title and body for announce activity" do
|
||||||
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
|
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
|
||||||
})
|
})
|
||||||
|
|
||||||
{:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
|
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
|
||||||
|
|
|
@ -106,7 +106,7 @@ test "it streams boosts of the user in the 'user' stream", %{user: user} do
|
||||||
|
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
|
||||||
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
|
{:ok, announce} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
|
assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
|
||||||
refute Streamer.filtered_by_user?(user, announce)
|
refute Streamer.filtered_by_user?(user, announce)
|
||||||
|
@ -427,7 +427,7 @@ test "it filters muted reblogs" do
|
||||||
{:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})
|
{:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})
|
||||||
|
|
||||||
Streamer.get_topic_and_add_socket("user", user1)
|
Streamer.get_topic_and_add_socket("user", user1)
|
||||||
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
{:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
|
||||||
assert_receive {:render_with_user, _, _, ^announce_activity}
|
assert_receive {:render_with_user, _, _, ^announce_activity}
|
||||||
assert Streamer.filtered_by_user?(user1, announce_activity)
|
assert Streamer.filtered_by_user?(user1, announce_activity)
|
||||||
end
|
end
|
||||||
|
@ -440,7 +440,7 @@ test "it filters reblog notification for reblog-muted actors" do
|
||||||
|
|
||||||
{:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
|
{:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
|
||||||
Streamer.get_topic_and_add_socket("user", user1)
|
Streamer.get_topic_and_add_socket("user", user1)
|
||||||
{:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
{:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)
|
||||||
|
|
||||||
assert_receive {:render_with_user, _, "notification.json", notif}
|
assert_receive {:render_with_user, _, "notification.json", notif}
|
||||||
assert Streamer.filtered_by_user?(user1, notif)
|
assert Streamer.filtered_by_user?(user1, notif)
|
||||||
|
|
|
@ -28,6 +28,7 @@ test "it sends new users digest emails" do
|
||||||
assert email.html_body =~ user.nickname
|
assert email.html_body =~ user.nickname
|
||||||
assert email.html_body =~ user2.nickname
|
assert email.html_body =~ user2.nickname
|
||||||
assert email.html_body =~ "cofe"
|
assert email.html_body =~ "cofe"
|
||||||
|
assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't fail when admin has no email" do
|
test "it doesn't fail when admin has no email" do
|
||||||
|
|
Loading…
Reference in a new issue