diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c672275e..dabc2a85a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
### Added
+- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
- Instance: Add `background_image` to configuration and `/api/v1/instance`
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` 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: 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.
- Mix task to create trusted OAuth App.
- Notifications: Added `follow_request` notification type.
@@ -47,7 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Healthcheck reporting the number of memory currently used, rather than allocated in total
-- `InsertSkeletonsForDeletedUsers` failing on some instances
+- `InsertSkeletonsForDeletedUsers` failing on some instances
## [2.0.3] - 2020-05-02
diff --git a/config/config.exs b/config/config.exs
index 1b11b4fa9..7385fb6c3 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -71,7 +71,8 @@
follow_redirect: true,
pool: :upload
]
- ]
+ ],
+ filename_display_max_length: 30
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
diff --git a/config/description.exs b/config/description.exs
index 716bcf4ff..807c945e0 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -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,
minimalScopesMode: false,
noAttachmentLinks: false,
- nsfwCensorImage: "",
+ nsfwCensorImage: "/static/img/nsfw.74818f9.png",
postContentType: "text/plain",
redirectRootLogin: "/main/friends",
redirectRootNoLogin: "/main/all",
@@ -1226,7 +1238,7 @@
type: :string,
description:
"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,
@@ -1346,6 +1358,12 @@
suggestions: [
:pleroma_fox_tan
]
+ },
+ %{
+ key: :default_user_avatar,
+ type: :string,
+ description: "URL of the default user avatar.",
+ suggestions: ["/images/avi.png"]
}
]
},
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index e8def466e..505acb293 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -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.
* `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.
+* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
!!! warning
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index bc842a59f..86409738a 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -147,6 +147,7 @@ def run(["gen" | rest]) do
"What directory should media uploads go in (when using the local uploader)?",
Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
)
+ |> Path.expand()
static_dir =
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.)?",
Pleroma.Config.get([:instance, :static_dir])
)
+ |> Path.expand()
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}.")
File.write(psql_path, result_psql)
- write_robots_txt(indexable, template_dir)
+ write_robots_txt(static_dir, indexable, template_dir)
shell_info(
"\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
- defp write_robots_txt(indexable, template_dir) do
+ defp write_robots_txt(static_dir, indexable, template_dir) do
robots_txt =
EEx.eval_file(
template_dir <> "/robots_txt.eex",
indexable: indexable
)
- static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
-
unless File.exists?(static_dir) do
File.mkdir_p!(static_dir)
end
diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex
index 7d16b807f..348cbac9c 100644
--- a/lib/pleroma/emails/new_users_digest_email.ex
+++ b/lib/pleroma/emails/new_users_digest_email.ex
@@ -14,8 +14,10 @@ def new_users(to, users_and_statuses) do
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
logo_url =
- Pleroma.Web.Endpoint.url() <>
- Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
+ Pleroma.Helpers.UriHelper.maybe_add_base(
+ Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo]),
+ Pleroma.Web.Endpoint.url()
+ )
new()
|> to({to.name, to.email})
diff --git a/lib/pleroma/helpers/uri_helper.ex b/lib/pleroma/helpers/uri_helper.ex
index 256252ddb..69d8c8fe0 100644
--- a/lib/pleroma/helpers/uri_helper.ex
+++ b/lib/pleroma/helpers/uri_helper.ex
@@ -24,4 +24,7 @@ def append_param_if_present(%{} = params, param_name, param_value) do
params
end
end
+
+ def maybe_add_base("/" <> uri, base), do: Path.join([base, uri])
+ def maybe_add_base(uri, _base), do: uri
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 224dfd023..d2eeeb479 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -305,8 +305,13 @@ def invisible?(_), do: false
def avatar_url(user, options \\ []) do
case user.avatar do
- %{"url" => [%{"href" => href} | _]} -> href
- _ -> !options[:no_default] && "#{Web.base_url()}/images/avi.png"
+ %{"url" => [%{"href" => href} | _]} ->
+ href
+
+ _ ->
+ unless options[:no_default] do
+ Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
+ end
end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index db2499b88..f325154da 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -363,36 +363,6 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
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()) ::
{:ok, Activity.t()} | {:error, any()}
def follow(follower, followed, activity_id \\ nil, local \\ true) do
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 62ad15d85..28727d619 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -21,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.FederatingPlug
alias Pleroma.Web.Federator
@@ -75,8 +76,8 @@ def user(conn, %{"nickname" => nickname}) do
end
end
- def object(conn, %{"uuid" => uuid}) do
- with ap_id <- o_status_url(conn, :object, uuid),
+ def object(conn, _) do
+ with ap_id <- Endpoint.url() <> conn.request_path,
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
{_, true} <- {:public?, Visibility.is_public?(object)} do
conn
@@ -101,8 +102,8 @@ def track_object_fetch(conn, object_id) do
conn
end
- def activity(conn, %{"uuid" => uuid}) do
- with ap_id <- o_status_url(conn, :activity, uuid),
+ def activity(conn, _params) do
+ with ap_id <- Endpoint.url() <> conn.request_path,
%Activity{} = activity <- Activity.normalize(ap_id),
{_, true} <- {:public?, Visibility.is_public?(activity)} do
conn
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 0107a8baa..a40054921 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ require Pleroma.Constants
+
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do
@@ -120,6 +122,29 @@ def like(actor, object) do
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()}
defp object_action(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"])
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index 7f1e0171c..c01c5f780 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
@@ -84,6 +85,16 @@ def validate(%{"type" => "Create", "object" => object} = create_activity, meta)
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
ChatMessageValidator.cast_and_apply(object)
end
@@ -116,7 +127,7 @@ def fetch_actor(object) do
def fetch_actor_and_object(object) do
fetch_actor(object)
- Object.normalize(object["object"])
+ Object.normalize(object["object"], true)
:ok
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
new file mode 100644
index 000000000..40f861f47
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
@@ -0,0 +1,101 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
index 657cdfdb1..0c54c4b23 100644
--- a/lib/pleroma/web/activity_pub/pipeline.ex
+++ b/lib/pleroma/web/activity_pub/pipeline.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Activity
+ alias Pleroma.Config
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -44,7 +45,7 @@ defp maybe_federate(%Object{}, _), do: {:ok, :not_federated}
defp maybe_federate(%Activity{} = activity, meta) 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
Federator.publish(activity)
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index 729c23af7..484178edd 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -4,9 +4,10 @@
defmodule Pleroma.Web.ActivityPub.Relay do
alias Pleroma.Activity
- alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.CommonAPI
require Logger
@relay_nickname "relay"
@@ -48,11 +49,11 @@ def unfollow(target_instance) do
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
with %User{} = user <- get_actor(),
- %Object{} = object <- Object.normalize(activity) do
- ActivityPub.announce(user, object, nil, true, false)
+ true <- Visibility.is_public?(activity) do
+ CommonAPI.repeat(activity.id, user)
else
error -> format_error(error)
end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 8e64b4615..f0f0659c2 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -42,6 +42,21 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
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
with undone_object <- Activity.get_by_ap_id(undone_object),
:ok <- handle_undoing(undone_object) do
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index afc63d6b7..a7f33c332 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -672,7 +672,8 @@ def handle_incoming(
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),
{:ok, activity, _meta} <-
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
- 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(
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
data,
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
similarity index 89%
rename from lib/pleroma/web/admin_api/admin_api_controller.ex
rename to lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index 647ceb3ba..6b1d64a2e 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -98,13 +98,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"], admin: true}
- when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
- )
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["write:statuses"], admin: true}
- when action in [:status_update, :status_delete]
+ when action in [:list_user_statuses, :list_instance_statuses]
)
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
user_delete(conn, %{"nicknames" => [nickname]})
@@ -597,16 +591,10 @@ def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params)
json_response(conn, :no_content, "")
else
{: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, _} ->
- 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
@@ -814,71 +802,6 @@ def report_notes_delete(%{assigns: %{user: user}} = conn, %{
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
{page, page_size} = page_params(params)
@@ -904,7 +827,7 @@ def config_descriptions(conn, _params) do
end
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)
conn
@@ -914,7 +837,7 @@ def config_show(conn, %{"only_db" => true}) do
end
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()
merged =
@@ -953,7 +876,7 @@ def config_show(conn, _params) do
end
def config_update(conn, %{"configs" => configs}) do
- with :ok <- configurable_from_database(conn) do
+ with :ok <- configurable_from_database() do
{_errors, results} =
configs
|> Enum.filter(&whitelisted_config?/1)
@@ -997,7 +920,7 @@ def config_update(conn, %{"configs" => configs}) do
end
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)
json(conn, %{})
@@ -1008,14 +931,11 @@ def need_reboot(conn, _params) do
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
end
- defp configurable_from_database(conn) do
+ defp configurable_from_database do
if Config.get(:configurable_from_database) do
:ok
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
@@ -1159,30 +1079,6 @@ def stats(conn, _) do
|> json(%{"status_visibility" => count})
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
{get_page(params["page"]), get_page_size(params["page_size"])}
end
diff --git a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex
new file mode 100644
index 000000000..82965936d
--- /dev/null
+++ b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex
new file mode 100644
index 000000000..08cb9c10b
--- /dev/null
+++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex
@@ -0,0 +1,79 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex
new file mode 100644
index 000000000..0b138dc79
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex
@@ -0,0 +1,165 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
index 7c08fbaa7..1a49fece0 100644
--- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation 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
@@ -46,7 +47,8 @@ def create_operation do
security: [%{"oAuth" => ["write:statuses"]}],
operationId: "EmojiReactionController.create",
responses: %{
- 200 => Operation.response("Status", "application/json", Status)
+ 200 => Operation.response("Status", "application/json", Status),
+ 400 => Operation.response("Bad Request", "application/json", ApiError)
}
}
end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
index 636c39a15..b0c8db863 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
@@ -8,6 +8,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
alias Pleroma.Web.ApiSpec.NotificationOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ import Pleroma.Web.ApiSpec.Helpers
+
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
@@ -17,10 +19,14 @@ def mark_as_read_operation do
%Operation{
tags: ["Notifications"],
summary: "Mark notifications as read. Query parameters are mutually exclusive.",
- parameters: [
- Operation.parameter(:id, :query, :string, "A single notification ID to read"),
- Operation.parameter(:max_id, :query, :string, "Read all notifications up to this id")
- ],
+ requestBody:
+ request_body("Parameters", %Schema{
+ 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"]}],
operationId: "PleromaAPI.NotificationController.mark_as_read",
responses: %{
diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex
index 0682ca6e5..ca9db01e5 100644
--- a/lib/pleroma/web/api_spec/operations/status_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/status_operation.ex
@@ -487,7 +487,7 @@ defp create_request do
}
end
- defp id_param do
+ def id_param do
Operation.parameter(:id, :path, FlakeID, "Status ID",
example: "9umDrYheeY451cQnEe",
required: true
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 7a2c0a96d..c08edbc5f 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -167,18 +167,19 @@ def delete(activity_id, user) do
end
def repeat(id, user, params \\ %{}) do
- with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
- object = Object.normalize(activity)
- announce_activity = Utils.get_existing_announce(user.ap_id, object)
- public = public_announce?(object, params)
-
- if announce_activity do
- {:ok, announce_activity, object}
- else
- ActivityPub.announce(user, object, nil, true, public)
- end
+ with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
+ object = %Object{} <- Object.normalize(activity, false),
+ {_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
+ 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
- _ -> {:error, :not_found}
+ {:existing_announce, %Activity{} = announce} ->
+ {:ok, announce}
+
+ _ ->
+ {:error, :not_found}
end
end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index ef7a9d967..15594125f 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -102,7 +102,8 @@ def get_to_and_cc(user, mentioned_users, inReplyTo, "private", _) do
end
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]), []}
else
{mentioned_users, []}
@@ -395,10 +396,12 @@ def to_masto_date(date) when is_binary(date) do
def to_masto_date(_), do: ""
defp shortname(name) do
- if String.length(name) < 30 do
- name
+ with max_length when max_length > 0 <-
+ Config.get([Pleroma.Upload, :filename_display_max_length], 30),
+ true <- String.length(name) > max_length do
+ String.slice(name, 0..max_length) <> "…"
else
- String.slice(name, 0..30) <> "…"
+ _ -> name
end
end
@@ -467,6 +470,8 @@ def maybe_notify_subscribers(
|> Enum.map(& &1.ap_id)
recipients ++ subscriber_ids
+ else
+ _e -> recipients
end
end
@@ -478,6 +483,8 @@ def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = ac
|> User.get_followers()
|> Enum.map(& &1.ap_id)
|> Enum.concat(recipients)
+ else
+ _e -> recipients
end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index 9dbf4f33c..83d997abd 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -210,7 +210,7 @@ def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
@doc "POST /api/v1/statuses/:id/reblog"
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
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
end
diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
index 98050487a..c498fe632 100644
--- a/lib/pleroma/web/mastodon_api/views/instance_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -23,7 +23,7 @@ def render("show.json", _) do
streaming_api: Pleroma.Web.Endpoint.websocket_url()
},
stats: Pleroma.Stats.get_stats(),
- thumbnail: Pleroma.Web.base_url() <> "/instance/thumbnail.jpeg",
+ thumbnail: instance_thumbnail(),
languages: ["en"],
registrations: Keyword.get(instance, :registrations_open),
# Extra (not present in Mastodon):
@@ -88,4 +88,9 @@ def federation do
end
|> Map.put(:enabled, Config.get([:instance, :federating]))
end
+
+ defp instance_thumbnail do
+ Pleroma.Config.get([:instance, :instance_thumbnail]) ||
+ "#{Pleroma.Web.base_url()}/instance/thumbnail.jpeg"
+ end
end
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 6971cd9f8..de1b0b3f0 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -32,13 +32,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do
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
ActivityPubController.call(conn, :object)
end
- def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
- with id <- o_status_url(conn, :object, uuid),
+ def object(%{assigns: %{format: format}} = conn, _params) do
+ with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <-
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do
@@ -54,13 +54,13 @@ def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
end
end
- def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
+ def activity(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do
ActivityPubController.call(conn, :activity)
end
- def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
- with id <- o_status_url(conn, :activity, uuid),
+ def activity(%{assigns: %{format: format}} = conn, _params) do
+ with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do
case format do
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
index a002912f3..19dcffdf3 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
@@ -22,6 +22,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
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
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
index 0b2f678c5..3ed8bd294 100644
--- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
@@ -14,7 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do
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
render(conn, "show.json", notification: notification, for: user)
else
@@ -25,7 +25,7 @@ def mark_as_read(%{assigns: %{user: user}} = conn, %{id: notification_id}) do
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 =
user
|> Notification.set_read_up_to(max_id)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index cd742a032..fef277ac6 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -189,10 +189,10 @@ defmodule Pleroma.Web.Router do
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
- get("/statuses/:id", AdminAPIController, :status_show)
- put("/statuses/:id", AdminAPIController, :status_update)
- delete("/statuses/:id", AdminAPIController, :status_delete)
- get("/statuses", AdminAPIController, :list_statuses)
+ get("/statuses/:id", StatusController, :show)
+ put("/statuses/:id", StatusController, :update)
+ delete("/statuses/:id", StatusController, :delete)
+ get("/statuses", StatusController, :index)
get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update)
@@ -564,6 +564,10 @@ defmodule Pleroma.Web.Router do
get("/notice/:id", OStatus.OStatusController, :notice)
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.UserController, :feed_redirect, as: :user_feed)
diff --git a/priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE b/priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE
new file mode 100644
index 000000000..eb5294eaf
--- /dev/null
+++ b/priv/static/READ_THIS_BEFORE_TOUCHING_FILES_HERE
@@ -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
diff --git a/test/fixtures/kroeg-announce-with-inline-actor.json b/test/fixtures/kroeg-announce-with-inline-actor.json
index 7bd6e8199..f73f93410 100644
--- a/test/fixtures/kroeg-announce-with-inline-actor.json
+++ b/test/fixtures/kroeg-announce-with-inline-actor.json
@@ -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":"
federated hacker teen
\n[she/they]
","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":"neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net
","type":"Person","url":"https://mastodon.social/@revenant"},"cc":"http://mastodon.example.org/users/admin/followers","content":"the name's jond (jeans bond)
","contentMap":{"en":"the name's jond (jeans bond)
"},"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" : "federated hacker teen
\n[she/they]
",
+ "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" : "neatly partitioned meats and cheeses appeal to me on an aesthetic level | any pronouns | revenant1.net
",
+ "type" : "Person",
+ "url" : "https://mastodon.social/@revenant"
+ },
+ "cc" : "http://mastodon.example.org/users/admin/followers",
+ "content" : "the name's jond (jeans bond)
",
+ "contentMap" : {
+ "en" : "the name's jond (jeans bond)
"
+ },
+ "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"
+}
diff --git a/test/fixtures/mastodon-note-object.json b/test/fixtures/mastodon-note-object.json
index 75bed9625..d28c7fbe9 100644
--- a/test/fixtures/mastodon-note-object.json
+++ b/test/fixtures/mastodon-note-object.json
@@ -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",
- "type": "Document",
- "name": null,
- "mediaType": "image/jpeg"
+ "Emoji" : "toot:Emoji",
+ "Hashtag" : "as:Hashtag",
+ "atomUri" : "ostatus:atomUri",
+ "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" : "yeah.
",
+ "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"
+}
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 111ff09f4..3a96721fa 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -648,7 +648,7 @@ test "it does not send notification to mentioned users in announces" do
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} =
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))
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
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))
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
assert length(Notification.for_user(user)) == 1
diff --git a/test/tasks/instance_test.exs b/test/tasks/instance_test.exs
index f6a4ba508..3b4c041d9 100644
--- a/test/tasks/instance_test.exs
+++ b/test/tasks/instance_test.exs
@@ -63,7 +63,7 @@ test "running gen" do
"--uploads-dir",
"test/uploads",
"--static-dir",
- "instance/static/"
+ "./test/../test/instance/static/"
])
end
@@ -83,6 +83,7 @@ test "running gen" do
assert generated_config =~ "configurable_from_database: true"
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
+ assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
end
defp generated_setup_psql do
diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs
index ca8daae44..b55aa1cdb 100644
--- a/test/tasks/user_test.exs
+++ b/test/tasks/user_test.exs
@@ -91,6 +91,7 @@ test "user is not created" do
describe "running rm" do
test "user is deleted" do
+ clear_config([:instance, :federating], true)
user = insert(:user)
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
user = insert(:user)
-
{:ok, post} = CommonAPI.post(user, %{status: "uguu"})
+
+ clear_config([:instance, :federating], true)
+
object = Object.normalize(post)
Object.prune(object)
diff --git a/test/user_test.exs b/test/user_test.exs
index 863e0106c..45125f704 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -992,7 +992,7 @@ test "works for announces" do
user = insert(:user, local: true)
{: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)
@@ -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_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, _user} = ObanHelpers.perform(job)
@@ -1777,4 +1777,16 @@ test "Notifications are updated", %{user: user} do
assert result.email_notifications["digest"] == false
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
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs
index c432c90e3..dd2a48a61 100644
--- a/test/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/web/activity_pub/activity_pub_controller_test.exs
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo
- import Pleroma.Factory
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Delivery
@@ -14,13 +13,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
alias Pleroma.Object
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Endpoint
alias Pleroma.Workers.ReceiverWorker
+ import Pleroma.Factory
+
+ require Pleroma.Constants
+
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@@ -168,6 +173,60 @@ test "it requires authentication if instance is NOT federating", %{
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
test "it returns a json representation of the object with accept application/json", %{
conn: conn
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 77bd07edf..3dcb62873 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -537,7 +537,7 @@ test "doesn't return blocked activities" do
assert Enum.member?(activities, activity_one)
{: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_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_three, _} = CommonAPI.repeat(activity_two.id, friend)
+ {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
activities =
ActivityPub.fetch_activities([], %{"blocking_user" => blocker})
@@ -618,7 +618,7 @@ test "doesn't return activities from blocked domains" do
followed_user = insert(: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 =
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"})
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
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 =
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"])
{: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_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, 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)])
@@ -846,7 +846,7 @@ test "doesn't return reblogs for users for whom reblogs have been muted" do
booster = insert(:user)
{: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})
@@ -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.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})
@@ -868,75 +868,6 @@ test "returns reblogs for users for whom reblogs have not been muted" do
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
test "copies the file to the configured folder" do
file = %Plug.Upload{
diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs
index f9990bd2c..929fdbc9b 100644
--- a/test/web/activity_pub/object_validator_test.exs
+++ b/test/web/activity_pub/object_validator_test.exs
@@ -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
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
diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs
index f3c437498..26557720b 100644
--- a/test/web/activity_pub/pipeline_test.exs
+++ b/test/web/activity_pub/pipeline_test.exs
@@ -9,6 +9,11 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do
import Pleroma.Factory
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
activity = insert(:note_activity)
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))
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
diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs
index 9e16e39c4..dbee8a0f4 100644
--- a/test/web/activity_pub/relay_test.exs
+++ b/test/web/activity_pub/relay_test.exs
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
use Pleroma.DataCase
alias Pleroma.Activity
- alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
@@ -95,21 +94,20 @@ test "returns error when object is unknown" do
end)
assert capture_log(fn ->
- assert Relay.publish(activity) == {:error, nil}
- end) =~ "[error] error: nil"
+ assert Relay.publish(activity) == {:error, false}
+ end) =~ "[error] error: false"
end
test_with_mock "returns announce activity and publish to federate",
Pleroma.Web.Federator,
[:passthrough],
[] do
- Pleroma.Config.put([:instance, :federating], true)
+ clear_config([:instance, :federating], true)
service_actor = Relay.get_actor()
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["actor"] == service_actor.ap_id
- assert activity.data["object"] == obj.data["id"]
assert called(Pleroma.Web.Federator.publish(activity))
end
@@ -117,13 +115,12 @@ test "returns error when object is unknown" do
Pleroma.Web.Federator,
[:passthrough],
[] do
- Pleroma.Config.put([:instance, :federating], false)
+ clear_config([:instance, :federating], false)
service_actor = Relay.get_actor()
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["actor"] == service_actor.ap_id
- assert activity.data["object"] == obj.data["id"]
refute called(Pleroma.Web.Federator.publish(activity))
end
end
diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs
index d63264de9..fb4411c07 100644
--- a/test/web/activity_pub/side_effects_test.exs
+++ b/test/web/activity_pub/side_effects_test.exs
@@ -173,7 +173,7 @@ test "when activation is required", %{delete: delete, user: user} do
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
{:ok, like} = CommonAPI.favorite(user, post.id)
{: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)
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
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
diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/web/activity_pub/transmogrifier/announce_handling_test.exs
new file mode 100644
index 000000000..e895636b5
--- /dev/null
+++ b/test/web/activity_pub/transmogrifier/announce_handling_test.exs
@@ -0,0 +1,172 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 0a54e3bb9..356004d48 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -28,6 +28,63 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
setup do: clear_config([:instance, :max_remote_account_fields])
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
activity = insert(:note_activity)
@@ -260,172 +317,6 @@ test "it works for incoming notices with to/cc not being an array (kroeg)" do
"henlo from my Psion netBook
message sent from my Psion netBook
"
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
user = insert(:user)
@@ -1188,7 +1079,7 @@ test "it inlines private announced objects" do
{: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)
@@ -1438,7 +1329,7 @@ test "it rejects activities which reference objects with bogus origins" do
}
assert capture_log(fn ->
- :error = Transmogrifier.handle_incoming(data)
+ {:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed"
end
@@ -1453,7 +1344,7 @@ test "it rejects activities which reference objects that have an incorrect attri
}
assert capture_log(fn ->
- :error = Transmogrifier.handle_incoming(data)
+ {:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed"
end
@@ -1468,7 +1359,7 @@ test "it rejects activities which reference objects that have an incorrect attri
}
assert capture_log(fn ->
- :error = Transmogrifier.handle_incoming(data)
+ {:error, _} = Transmogrifier.handle_incoming(data)
end) =~ "Object containment failed"
end
end
diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs
index 9e0a0f1c4..15f03f193 100644
--- a/test/web/activity_pub/utils_test.exs
+++ b/test/web/activity_pub/utils_test.exs
@@ -334,7 +334,7 @@ test "fetches existing announce" do
assert object = Object.normalize(note_activity)
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
end
end
diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs
index 43f0617f0..f0389845d 100644
--- a/test/web/activity_pub/views/object_view_test.exs
+++ b/test/web/activity_pub/views/object_view_test.exs
@@ -73,7 +73,7 @@ test "renders an announce activity" do
object = Object.normalize(note)
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})
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs
similarity index 95%
rename from test/web/admin_api/admin_api_controller_test.exs
rename to test/web/admin_api/controllers/admin_api_controller_test.exs
index 370d876d0..321840a8c 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/controllers/admin_api_controller_test.exs
@@ -148,6 +148,7 @@ test "GET /api/pleroma/admin/users/:nickname requires " <>
describe "DELETE /api/pleroma/admin/users" do
test "single user", %{admin: admin, conn: conn} do
user = insert(:user)
+ clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
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}")
- assert "Not found" == json_response(conn, 404)
+ assert %{"error" => "Not found"} == json_response(conn, 404)
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")
assert json_response(conn, :bad_request) ==
- "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
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")
assert json_response(conn, :bad_request) ==
- "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."
+ }
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"})
|> json_response(404)
- assert response == "Not found"
+ assert response == %{"error" => "Not found"}
end
end
@@ -1413,7 +1420,7 @@ test "with token", %{conn: conn} do
test "with invalid token", %{conn: conn} do
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
@@ -1440,7 +1447,7 @@ test "returns report by its id", %{conn: conn} do
test "returns 404 when report id is invalid", %{conn: conn} do
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
@@ -1697,115 +1704,6 @@ test "returns 403 when requested by anonymous" do
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
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")
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
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" => []})
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
describe "POST /api/pleroma/admin/config" do
@@ -2944,6 +2844,7 @@ test "proxy tuple ip", %{conn: conn} do
assert ":proxy_url" in db
end
+ @tag capture_log: true
test "doesn't set keys not in the whitelist", %{conn: conn} do
clear_config(:database_config_whitelist, [
{:pleroma, :key1},
@@ -2998,54 +2899,6 @@ test "need_reboot flag", %{conn: conn} do
on_exit(fn -> Restarter.Pleroma.refresh() 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
setup do
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
other_user = insert(:user)
{: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")
assert json_response(conn_res, 200) |> length() == 0
diff --git a/test/web/admin_api/controllers/status_controller_test.exs b/test/web/admin_api/controllers/status_controller_test.exs
new file mode 100644
index 000000000..124d8dc2e
--- /dev/null
+++ b/test/web/admin_api/controllers/status_controller_test.exs
@@ -0,0 +1,194 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 9e626fb9e..9e129e5a7 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -116,6 +116,8 @@ test "it works with pruned objects" do
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
+ clear_config([:instance, :federating], true)
+
Object.normalize(post, false)
|> Object.prune()
@@ -134,6 +136,8 @@ test "it allows users to delete their posts" do
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
+ clear_config([:instance, :federating], true)
+
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
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
+ 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
user = insert(: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.repeat(activity.id, user)
+ {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
+ assert Visibility.is_public?(announce_activity)
end
test "can't repeat a repeat" do
@@ -499,9 +530,9 @@ test "can't repeat a repeat" do
other_user = insert(:user)
{: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
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{} = announce_activity, _} =
+ {:ok, %Activity{} = announce_activity} =
CommonAPI.repeat(activity.id, user, %{visibility: "private"})
assert Visibility.is_private?(announce_activity)
+ refute Visibility.visible_for_user?(announce_activity, nil)
end
test "favoriting a status" do
@@ -533,8 +565,8 @@ test "retweeting a status twice returns the status" do
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
- {:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
- {:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
+ {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
+ {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
end
test "favoriting a status twice returns ok, but without the like activity" do
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index 5708db6a4..e67c10b93 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -14,18 +14,41 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
@public_address "https://www.w3.org/ns/activitystreams#Public"
- test "it adds attachment links to a given text and attachment set" do
- 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"
+ describe "add_attachments/2" do
+ setup do
+ name =
+ "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 = %{
- "url" => [%{"href" => name}]
- }
+ attachment = %{
+ "url" => [%{"href" => URI.encode(name)}]
+ }
- res = Utils.add_attachments("", [attachment])
+ %{name: name, attachment: attachment}
+ end
- assert res ==
- "
Sakura Mana – Turned on by a Se…"
+ test "it adds attachment links to a given text and attachment set", %{
+ name: name,
+ attachment: attachment
+ } do
+ len = 10
+ clear_config([Pleroma.Upload, :filename_display_max_length], len)
+
+ expected =
+ "
#{String.slice(name, 0..len)}…"
+
+ 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 = "
#{name}"
+
+ assert Utils.add_attachments("", [attachment]) == expected
+ end
end
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)
- assert length(to) == 3
+ assert length(to) == 2
assert Enum.empty?(cc)
assert mentioned_user.ap_id in to
- assert third_user.ap_id in to
assert user.follower_address in to
end
@@ -327,6 +349,15 @@ test "for direct posts, a reply" do
{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 Enum.empty?(cc)
diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs
index 280bd6aca..1ce97378d 100644
--- a/test/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller_test.exs
@@ -256,7 +256,7 @@ test "respects blocks", %{user: user_one, conn: conn} do
User.block(user_one, user_two)
{: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 =
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
{: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")
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)
{: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 [] ==
conn
diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs
index 562fc4d8e..e278d61f5 100644
--- a/test/web/mastodon_api/controllers/notification_controller_test.exs
+++ b/test/web/mastodon_api/controllers/notification_controller_test.exs
@@ -280,8 +280,8 @@ test "filters notifications for Announce activities" do
{:ok, unlisted_activity} =
CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"})
- {:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
- {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
+ {:ok, _} = CommonAPI.repeat(public_activity.id, user)
+ {:ok, _} = CommonAPI.repeat(unlisted_activity.id, user)
activity_ids =
conn
@@ -301,7 +301,7 @@ test "filters notifications using exclude_types" do
{:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
{: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)
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, create_activity} = CommonAPI.post(user, %{status: "hey"})
{: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)
mention_notification_id = get_notification_id_by_activity(mention_activity)
diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs
index 962e64b03..700c82e4f 100644
--- a/test/web/mastodon_api/controllers/status_controller_test.exs
+++ b/test/web/mastodon_api/controllers/status_controller_test.exs
@@ -878,8 +878,8 @@ test "reblogged status for another user" do
user3 = insert(:user)
{:ok, _} = CommonAPI.favorite(user2, activity.id)
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
- {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
- {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
+ {:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
+ {:ok, _} = CommonAPI.repeat(activity.id, user2)
conn_res =
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
activity = insert(:note_activity)
- {:ok, _, _} = CommonAPI.repeat(activity.id, user)
+ {:ok, _} = CommonAPI.repeat(activity.id, user)
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
other_user = insert(:user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
@@ -1458,7 +1458,7 @@ test "does not return users who have reblogged the status but are blocked", %{
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
@@ -1469,12 +1469,12 @@ test "does not return users who have reblogged the status but are blocked", %{
end
test "does not return users who have reblogged the status privately", %{
- conn: conn,
- activity: activity
+ conn: conn
} do
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 =
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
other_user = insert(:user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
build_conn()
diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs
index 8db6a2379..384fe7253 100644
--- a/test/web/mastodon_api/views/notification_view_test.exs
+++ b/test/web/mastodon_api/views/notification_view_test.exs
@@ -104,7 +104,7 @@ test "Reblog notification" do
user = insert(:user)
another_user = insert(:user)
{: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)
reblog_activity = Activity.get_by_id(create_activity.id)
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index 43e3bdca1..5cbadf0fc 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -442,7 +442,7 @@ test "a reblog" do
user = insert(:user)
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})
@@ -600,7 +600,7 @@ test "does not embed a relationship in the account in reposts" do
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})
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index bb349cb19..ee498f4b5 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -10,7 +10,11 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
alias Pleroma.Config
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Endpoint
+
+ require Pleroma.Constants
setup_all do
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)
+ 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
describe "GET /objects/:uuid (text/html)" do
setup %{conn: conn} do
diff --git a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
index ee66ebf87..e1bb5ebfe 100644
--- a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
+++ b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
@@ -33,6 +33,13 @@ test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
assert result["pleroma"]["emoji_reactions"] == [
%{"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
test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
diff --git a/test/web/pleroma_api/controllers/notification_controller_test.exs b/test/web/pleroma_api/controllers/notification_controller_test.exs
index 7c5ace804..bb4fe6c49 100644
--- a/test/web/pleroma_api/controllers/notification_controller_test.exs
+++ b/test/web/pleroma_api/controllers/notification_controller_test.exs
@@ -23,7 +23,8 @@ test "it marks a single notification as read", %{user: user1, conn: conn} do
response =
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)
assert %{"pleroma" => %{"is_seen" => true}} = response
@@ -41,7 +42,8 @@ test "it marks multiple notifications as read", %{user: user1, conn: conn} do
[response1, response2] =
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)
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
response =
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)
assert response == %{"error" => "Cannot get notification"}
diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs
index 2acd0939f..a826b24c9 100644
--- a/test/web/push/impl_test.exs
+++ b/test/web/push/impl_test.exs
@@ -151,7 +151,7 @@ test "renders title and body for announce activity" do
"Lorem ipsum dolor sit amet, 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)
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs
index 95b7d1420..cb4595bb6 100644
--- a/test/web/streamer/streamer_test.exs
+++ b/test/web/streamer/streamer_test.exs
@@ -106,7 +106,7 @@ test "it streams boosts of the user in the 'user' stream", %{user: user} do
other_user = insert(:user)
{: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}
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"})
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 Streamer.filtered_by_user?(user1, announce_activity)
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"})
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 Streamer.filtered_by_user?(user1, notif)
diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/workers/cron/new_users_digest_worker_test.exs
index 54cf0ca46..ee589bb55 100644
--- a/test/workers/cron/new_users_digest_worker_test.exs
+++ b/test/workers/cron/new_users_digest_worker_test.exs
@@ -28,6 +28,7 @@ test "it sends new users digest emails" do
assert email.html_body =~ user.nickname
assert email.html_body =~ user2.nickname
assert email.html_body =~ "cofe"
+ assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
end
test "it doesn't fail when admin has no email" do