Merge remote-tracking branch 'origin/develop' into translation-provider-translatelocally

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk 2025-11-28 15:06:50 +01:00
commit 13bc4ba639
38 changed files with 418 additions and 147 deletions

View file

@ -16,9 +16,15 @@ workflow:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH == "stable"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
# Default artifacts configuration
.default_artifacts: &default_artifacts
expire_in: 30 days
cache: &global_cache_policy
key: $CI_JOB_IMAGE-$CI_COMMIT_SHORT_SHA
paths:
@ -56,6 +62,7 @@ check-changelog:
before_script: ''
after_script: ''
cache: {}
artifacts: *default_artifacts
script:
- apk add git
- sh ./tools/check-changelog
@ -71,6 +78,7 @@ check-changelog:
.using-ci-base:
tags:
- amd64
artifacts: *default_artifacts
build-1.15.8-otp-26:
extends:
@ -101,8 +109,12 @@ spec-build:
artifacts:
paths:
- spec.json
reports:
dotenv: build.env
expire_in: 42 years
script:
- mix pleroma.openapi_spec spec.json
- echo "SPEC_BUILD_JOB_ID=$CI_JOB_ID" >> build.env
benchmark:
extends:
@ -153,6 +165,7 @@ unit-testing-1.15.8-otp-26:
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --cover --preload-modules"
coverage: '/^Line total: ([^ ]*%)$/'
artifacts:
expire_in: 30 days
reports:
coverage_report:
coverage_format: cobertura
@ -171,6 +184,7 @@ unit-testing-1.18.3-otp-27:
formatting-1.15:
extends: .build_changes_policy
artifacts: *default_artifacts
image: &formatting_elixir elixir:1.15-alpine
stage: lint
cache: *testing_cache_policy
@ -185,6 +199,7 @@ formatting-1.15:
cycles-1.15:
extends: .build_changes_policy
artifacts: *default_artifacts
image: *formatting_elixir
stage: lint
cache: {}
@ -208,7 +223,7 @@ dialyzer:
- .using-ci-base
stage: lint
allow_failure: true
when: manual
when: manual
cache: *testing_cache_policy
tags:
- feld
@ -217,15 +232,13 @@ dialyzer:
docs-deploy:
stage: deploy
cache: *testing_cache_policy
image: alpine:latest
trigger:
project: pleroma/docs
branch: master
strategy: depend
only:
- stable@pleroma/pleroma
- develop@pleroma/pleroma
before_script:
- apk add curl
script:
- curl --fail-with-body -X POST -F"token=$DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" https://git.pleroma.social/api/v4/projects/673/trigger/pipeline
review_app:
image: alpine:3.9
stage: deploy
@ -241,6 +254,7 @@ review_app:
except:
- master
- develop
artifacts: *default_artifacts
script:
- echo "$CI_ENVIRONMENT_SLUG"
- mkdir -p ~/.ssh
@ -257,21 +271,19 @@ review_app:
spec-deploy:
stage: deploy
artifacts:
paths:
- spec.json
trigger:
project: pleroma/api-docs
branch: master
strategy: depend
only:
- develop@pleroma/pleroma
image: alpine:latest
before_script:
- apk add curl
script:
- curl --fail-with-body -X POST -F"token=$API_DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" -F"variables[JOB_REF]=$CI_JOB_ID" https://git.pleroma.social/api/v4/projects/1130/trigger/pipeline
variables:
SPEC_BUILD_JOB_ID: $SPEC_BUILD_JOB_ID
stop_review_app:
image: alpine:3.9
stage: deploy
artifacts: *default_artifacts
before_script:
- apk update && apk add openssh-client git
when: manual

View file

@ -0,0 +1 @@
Fix fetching public keys with authorized fetch enabled

View file

1
changelog.d/docs.skip Normal file
View file

@ -0,0 +1 @@
Update *Differences in Mastodon API responses from vanilla Mastodon*

View file

@ -0,0 +1 @@
Support new Mastodon API for endorsed accounts

View file

@ -0,0 +1 @@
Fix publisher when publishing to a list of users

View file

@ -0,0 +1 @@
Add `timelines_access` to InstanceView

View file

@ -0,0 +1 @@
Use end-of-string in regex for local `get_by_nickname`

View file

1
changelog.d/plaroma.skip Normal file
View file

@ -0,0 +1 @@
i don't think it's called plaroma

View file

@ -0,0 +1 @@
`remote_url` links to unproxied URL

View file

@ -0,0 +1 @@
Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom

View file

@ -0,0 +1 @@
Send push notifications for statuses from subscribed accounts

View file

@ -0,0 +1 @@
Support Mozhi translation provider

View file

@ -3559,6 +3559,7 @@ config :pleroma, :config_description, [
suggestions: [
Pleroma.Language.Translation.Deepl,
Pleroma.Language.Translation.Libretranslate,
Pleroma.Language.Translation.Mozhi,
Pleroma.Language.Translation.TranslateLocally
]
},
@ -3592,11 +3593,24 @@ config :pleroma, :config_description, [
},
%{
group: {:subgroup, Pleroma.Language.Translation.TranslateLocally},
key: :intermediate_language,
key: :intermediary_language,
label:
"translateLocally intermediate language (used when direct source->target model is not available)",
"translateLocally intermediary language (used when direct source->target model is not available)",
type: :string,
suggestions: ["en"]
},
%{
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
key: :base_url,
label: "Mozhi instance URL",
type: :string
},
%{
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
key: :engine,
label: "Engine used for Mozhi",
type: :string,
suggestions: ["libretranslate"]
}
]
}

View file

@ -40,10 +40,13 @@ Has these additional fields under the `pleroma` object:
- `parent_visible`: If the parent of this post is visible to the user or not.
- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
- `quotes_count`: the count of status quotes.
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
- `list_id`: the ID of the list the post is addressed to (if any, only returned to author).
Has these additional fields under the `poll.pleroma` object:
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
- `content_type`: The content type of the status source.
@ -98,6 +101,9 @@ Endpoints which accept `with_relationships` parameter:
- `/api/v1/accounts/:id/followers`
- `/api/v1/accounts/:id/following`
- `/api/v1/mutes`
- `/api/v1/blocks`
- `/api/v1/search`
- `/api/v2/search`
Has these additional fields under the `pleroma` object:

View file

@ -16,7 +16,7 @@ Note: the packages are not required with the current default settings of Pleroma
It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`)
* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Pleroma.Upload/filters` in `config/config.exs`)
* Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`)
## `ffmpeg`
@ -33,5 +33,5 @@ It is required for the following Pleroma features:
It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`)
* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`)
* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)
* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)

View file

@ -0,0 +1,109 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Language.Translation.Mozhi do
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
alias Pleroma.Language.Translation.Provider
use Provider
@behaviour Provider
@name "Mozhi"
@impl Provider
def configured?, do: not_empty_string(base_url()) and not_empty_string(engine())
@impl Provider
def translate(content, source_language, target_language) do
endpoint =
base_url()
|> URI.merge("/api/translate")
|> URI.to_string()
case Pleroma.HTTP.get(
endpoint <>
"?" <>
URI.encode_query(%{
engine: engine(),
text: content,
from: source_language,
to: target_language
}),
[{"Accept", "application/json"}]
) do
{:ok, %{status: 200} = res} ->
%{
"translated-text" => content,
"source_language" => source_language
} = Jason.decode!(res.body)
{:ok,
%{
content: content,
detected_source_language: source_language,
provider: @name
}}
_ ->
{:error, :internal_server_error}
end
end
@impl Provider
def supported_languages(type) when type in [:source, :target] do
path =
case type do
:source -> "/api/source_languages"
:target -> "/api/target_languages"
end
endpoint =
base_url()
|> URI.merge(path)
|> URI.to_string()
case Pleroma.HTTP.get(
endpoint <>
"?" <>
URI.encode_query(%{
engine: engine()
}),
[{"Accept", "application/json"}]
) do
{:ok, %{status: 200} = res} ->
languages =
Jason.decode!(res.body)
|> Enum.map(fn %{"Id" => language} -> language end)
{:ok, languages}
_ ->
{:error, :internal_server_error}
end
end
@impl Provider
def languages_matrix do
with {:ok, source_languages} <- supported_languages(:source),
{:ok, target_languages} <- supported_languages(:target) do
{:ok,
Map.new(source_languages, fn language -> {language, target_languages -- [language]} end)}
else
{:error, error} -> {:error, error}
end
end
@impl Provider
def name, do: @name
defp base_url do
Pleroma.Config.get([__MODULE__, :base_url])
end
defp engine do
Pleroma.Config.get([__MODULE__, :engine])
end
end

View file

@ -526,9 +526,7 @@ defmodule Pleroma.Notification do
%Activity{data: %{"type" => "Create"}} = activity,
local_only
) do
notification_enabled_ap_ids =
[]
|> Utils.maybe_notify_subscribers(activity)
notification_enabled_ap_ids = Utils.get_notified_subscribers(activity)
potential_receivers =
User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only)

View file

@ -54,7 +54,7 @@ defmodule Pleroma.Signature do
def fetch_public_key(conn) do
with {:ok, actor_id} <- get_actor_id(conn),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
{:ok, public_key} <- User.get_or_fetch_public_key_for_ap_id(actor_id) do
{:ok, public_key}
else
e ->

View file

@ -233,8 +233,8 @@ defmodule Pleroma.User do
for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
@user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
# `def subscriber_users/2`, `def endorsed_users_relation/2`
# `def reblog_muted_users_relation/2`, `def notification_muted_users_relation/2`,
# `def subscriber_users_relation/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target))
@ -1357,7 +1357,7 @@ defmodule Pleroma.User do
@spec get_by_nickname(String.t()) :: User.t() | nil
def get_by_nickname(nickname) do
Repo.get_by(User, nickname: nickname) ||
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()}$)i, nickname) do
Repo.get_by(User, nickname: local_nickname(nickname))
end
end
@ -2307,6 +2307,15 @@ defmodule Pleroma.User do
def public_key(_), do: {:error, "key not found"}
def get_or_fetch_public_key_for_ap_id(ap_id) do
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
{:ok, public_key} <- public_key(user) do
{:ok, public_key}
else
_ -> :error
end
end
def get_public_key_for_ap_id(ap_id) do
with %User{} = user <- get_cached_by_ap_id(ap_id),
{:ok, public_key} <- public_key(user) do

View file

@ -331,17 +331,21 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Repo.checkout(fn ->
Enum.each([priority_inboxes, other_inboxes], fn inboxes ->
Enum.each(inboxes, fn inbox ->
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
{%User{ap_id: ap_id}, priority} =
get_user_with_priority(inbox, priority_recipients, recipients)
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
# instance would only accept a first message for the first recipient and ignore the rest.
cc = get_cc_ap_ids(ap_id, recipients)
__MODULE__.enqueue_one(%{
inbox: inbox,
cc: cc,
activity_id: activity.id
})
__MODULE__.enqueue_one(
%{
inbox: inbox,
cc: cc,
activity_id: activity.id
},
priority: priority
)
end)
end)
end)
@ -403,4 +407,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
end
def gather_nodeinfo_protocol_names, do: ["activitypub"]
defp get_user_with_priority(inbox, priority_recipients, recipients) do
[{priority_recipients, 0}, {recipients, 1}]
|> Enum.find_value(fn {recipients, priority} ->
with %User{} = user <- Enum.find(recipients, fn actor -> actor.inbox == inbox end) do
{user, priority}
else
_ -> nil
end
end)
end
end

View file

@ -398,6 +398,28 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
def endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
description: "Returns endorsed accounts",
operationId: "AccountController.endorsements",
parameters: [
with_relationships_param(),
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}
],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def remove_from_followers_operation do
%Operation{
tags: ["Account actions"],
@ -500,11 +522,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
def endorsements_operation do
def own_endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
operationId: "AccountController.endorsements",
operationId: "AccountController.own_endorsements",
description: "Returns endorsed accounts",
security: [%{"oAuth" => ["read:accounts"]}],
responses: %{

View file

@ -64,25 +64,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
}
end
def endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
description: "Returns endorsed accounts",
operationId: "PleromaAPI.AccountController.endorsements",
parameters: [with_relationships_param(), id_param()],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
AccountOperation.array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def subscribe_operation do
%Operation{
deprecated: true,

View file

@ -402,28 +402,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
def maybe_notify_subscribers(
recipients,
%Activity{data: %{"actor" => actor, "type" => "Create"}} = activity
) do
# Do not notify subscribers if author is making a reply
with %Object{data: object} <- Object.normalize(activity, fetch: false),
nil <- object["inReplyTo"],
%User{} = user <- User.get_cached_by_ap_id(actor) do
subscriber_ids =
user
|> User.subscriber_users()
|> Enum.filter(&Visibility.visible_for_user?(activity, &1))
|> Enum.map(& &1.ap_id)
recipients ++ subscriber_ids
else
_e -> recipients
end
end
def maybe_notify_subscribers(recipients, _), do: recipients
def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do
with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do
user
@ -437,6 +415,27 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def maybe_notify_followers(recipients, _), do: recipients
def get_notified_subscribers(
%Activity{data: %{"actor" => actor, "type" => "Create"}} = activity
) do
# Do not notify subscribers if author is making a reply
with %Object{data: object} <- Object.normalize(activity, fetch: false),
nil <- object["inReplyTo"],
%User{} = user <- User.get_cached_by_ap_id(actor) do
subscriber_ids =
user
|> User.subscriber_users()
|> Enum.filter(&Visibility.visible_for_user?(activity, &1))
|> Enum.map(& &1.ap_id)
subscriber_ids
else
_e -> []
end
end
def get_notified_subscribers(_), do: []
def maybe_extract_mentions(%{"tag" => tag}) do
tag
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)

View file

@ -28,9 +28,12 @@ defmodule Pleroma.Web.Feed.UserController do
ActivityPubController.call(conn, :user)
end
def feed_redirect(conn, %{"nickname" => nickname}) do
def feed_redirect(%{assigns: assigns} = conn, %{"nickname" => nickname}) do
format = Map.get(assigns, :format, "atom")
format = if format in ["atom", "rss"], do: format, else: "atom"
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.atom")
redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.#{format}")
end
end

View file

@ -38,7 +38,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
when action in [:show, :followers, :following]
when action in [:show, :followers, :following, :endorsements]
)
plug(
@ -50,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{scopes: ["read:accounts"]}
when action in [:verify_credentials, :endorsements]
when action in [:verify_credentials, :endorsements, :own_endorsements]
)
plug(
@ -89,7 +89,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@relationship_actions [:follow, :unfollow, :remove_from_followers]
@needs_account ~W(
followers following lists follow unfollow mute unmute block unblock
note endorse unendorse remove_from_followers
note endorse unendorse endorsements remove_from_followers
)a
plug(
@ -555,6 +555,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
@doc "GET /api/v1/accounts/:id/endorsements"
def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
|> Pleroma.Repo.all()
conn
|> render("index.json",
for: for_user,
users: users,
as: :user,
embed_relationships: embed_relationships?(params)
)
end
@doc "POST /api/v1/accounts/:id/remove_from_followers"
def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
{:error, "Can not unfollow yourself"}
@ -631,7 +647,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
@doc "GET /api/v1/endorsements"
def endorsements(%{assigns: %{user: user}} = conn, params) do
def own_endorsements(%{assigns: %{user: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)

View file

@ -257,10 +257,34 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
vapid: %{
public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
},
translation: %{enabled: Pleroma.Language.Translation.configured?()}
translation: %{enabled: Pleroma.Language.Translation.configured?()},
timelines_access: %{
live_feeds: timelines_access(),
hashtag_feeds: timelines_access(),
# not implemented in Pleroma
trending_link_feeds: %{
local: "disabled",
remote: "disabled"
}
}
})
end
defp timelines_access do
%{
local: timeline_access(:local),
remote: timeline_access(:federated)
}
end
defp timeline_access(kind) do
if Config.restrict_unauthenticated_access?(:timelines, kind) do
"authenticated"
else
"public"
end
end
defp pleroma_configuration(instance) do
base_urls = %{}

View file

@ -602,7 +602,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def render("attachment.json", %{attachment: attachment}) do
[attachment_url | _] = attachment["url"]
media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
href = attachment_url["href"] |> MediaProxy.url()
href_remote = attachment_url["href"]
href = href_remote |> MediaProxy.url()
href_preview = attachment_url["href"] |> MediaProxy.preview_url()
meta = render("attachment_meta.json", %{attachment: attachment})
@ -641,7 +642,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
%{
id: attachment_id,
url: href,
remote_url: href,
remote_url: href_remote,
preview_url: href_preview,
text_url: href,
type: type,

View file

@ -9,7 +9,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
only: [
json_response: 3,
add_link_headers: 2,
embed_relationships?: 1,
assign_account_by_id: 2
]
@ -45,12 +44,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
)
plug(
OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
when action == :endorsements
)
plug(
OAuthScopesPlug,
%{scopes: ["read:accounts"]} when action == :birthdays
@ -60,7 +53,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
plug(
:assign_account_by_id
when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
when action in [:favourites, :subscribe, :unsubscribe]
)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
@ -109,22 +102,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
)
end
@doc "GET /api/v1/pleroma/accounts/:id/endorsements"
def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
|> Pleroma.Repo.all()
conn
|> render("index.json",
for: for_user,
users: users,
as: :user,
embed_relationships: embed_relationships?(params)
)
end
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do

View file

@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do
end
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
@supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
@supported_alert_types ~w[follow favourite mention status reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
defp alerts(%{data: %{alerts: alerts}}) do
alerts = Map.take(alerts, @supported_alert_types)

View file

@ -608,7 +608,6 @@ defmodule Pleroma.Web.Router do
scope [] do
pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites)
get("/accounts/:id/endorsements", AccountController, :endorsements)
get("/statuses/:id/quotes", StatusController, :quotes)
end
@ -637,6 +636,11 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/scrobbles", ScrobbleController, :index)
end
scope "/api/v1/pleroma", Pleroma.Web.MastodonAPI do
pipe_through(:api)
get("/accounts/:id/endorsements", AccountController, :endorsements)
end
scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
scope [] do
pipe_through(:authenticated_api)
@ -653,7 +657,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/relationships", AccountController, :relationships)
get("/accounts/familiar_followers", AccountController, :familiar_followers)
get("/accounts/:id/lists", AccountController, :lists)
get("/endorsements", AccountController, :endorsements)
get("/endorsements", AccountController, :own_endorsements)
get("/blocks", AccountController, :blocks)
get("/mutes", AccountController, :mutes)
@ -667,6 +671,8 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/note", AccountController, :note)
post("/accounts/:id/pin", AccountController, :endorse)
post("/accounts/:id/unpin", AccountController, :unendorse)
post("/accounts/:id/endorse", AccountController, :endorse)
post("/accounts/:id/unendorse", AccountController, :unendorse)
post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers)
get("/conversations", ConversationController, :index)
@ -782,6 +788,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/statuses", AccountController, :statuses)
get("/accounts/:id/followers", AccountController, :followers)
get("/accounts/:id/following", AccountController, :following)
get("/accounts/:id/endorsements", AccountController, :endorsements)
get("/accounts/:id", AccountController, :show)
post("/accounts", AccountController, :create)

View file

@ -1881,6 +1881,11 @@ defmodule Pleroma.UserTest do
end
end
test "get_or_fetch_public_key_for_ap_id fetches a user that's not in the db" do
assert {:ok, _key} =
User.get_or_fetch_public_key_for_ap_id("http://mastodon.example.org/users/admin")
end
test "get_public_key_for_ap_id returns correctly for user that's not in the db" do
assert :error = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
end

View file

@ -332,6 +332,33 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
)
end
test "activity with BCC is published to a list member." do
actor = insert(:user)
{:ok, list} = Pleroma.List.create("list", actor)
list_member = insert(:user, %{local: false})
Pleroma.List.follow(list, list_member)
note_activity =
insert(:note_activity,
# recipients: [follower.ap_id],
data_attrs: %{"bcc" => [list.ap_id]}
)
res = Publisher.publish(actor, note_activity)
assert res == :ok
assert_enqueued(
worker: "Pleroma.Workers.PublisherWorker",
args: %{
"params" => %{
inbox: list_member.inbox,
activity_id: note_activity.id
}
}
)
end
test "publishes a delete activity to peers who signed fetch requests to the create acitvity/object." do
fetcher =
insert(:user,

View file

@ -282,6 +282,21 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
"#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}/feed.atom"
end
test "redirects to rss feed when explicitly requested", %{conn: conn} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
conn =
conn
|> put_req_header("accept", "application/xml")
|> get("/users/#{user.nickname}.rss")
assert conn.status == 302
assert redirected_to(conn) ==
"#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}/feed.rss"
end
test "with non-html / non-json format, it returns error when user is not found", %{conn: conn} do
response =
conn

View file

@ -2134,7 +2134,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert %{"id" => ^id1, "endorsed" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> post("/api/v1/accounts/#{id1}/endorse")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id1}] =
@ -2153,7 +2153,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert %{"id" => ^id1, "endorsed" => false} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/unpin")
|> post("/api/v1/accounts/#{id1}/unendorse")
|> json_response_and_validate_schema(200)
assert [] =
@ -2172,15 +2172,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{id1}/pin")
|> post("/api/v1/accounts/#{id1}/endorse")
|> json_response_and_validate_schema(200)
assert %{"error" => "You have already pinned the maximum number of users"} =
conn
|> assign(:user, user)
|> post("/api/v1/accounts/#{id2}/pin")
|> post("/api/v1/accounts/#{id2}/endorse")
|> json_response_and_validate_schema(400)
end
test "returns a list of pinned accounts", %{conn: conn} do
clear_config([:instance, :max_endorsed_users], 3)
%{id: id1} = user1 = insert(:user)
%{id: id2} = user2 = insert(:user)
%{id: id3} = user3 = insert(:user)
CommonAPI.follow(user2, user1)
CommonAPI.follow(user3, user1)
User.endorse(user1, user2)
User.endorse(user1, user3)
[%{"id" => ^id2}, %{"id" => ^id3}] =
conn
|> get("/api/v1/accounts/#{id1}/endorsements")
|> json_response_and_validate_schema(200)
end
test "returns 404 error when specified user is not exist", %{conn: conn} do
conn = get(conn, "/api/v1/accounts/test/endorsements")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "familiar followers" do

View file

@ -194,4 +194,28 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
refute Map.has_key?(result["pleroma"]["metadata"]["base_urls"], "media_proxy")
refute Map.has_key?(result["pleroma"]["metadata"]["base_urls"], "upload")
end
test "display timeline access restrictions", %{conn: conn} do
clear_config([:restrict_unauthenticated, :timelines, :local], true)
clear_config([:restrict_unauthenticated, :timelines, :federated], false)
conn = get(conn, "/api/v2/instance")
assert result = json_response_and_validate_schema(conn, 200)
assert result["configuration"]["timelines_access"] == %{
"live_feeds" => %{
"local" => "authenticated",
"remote" => "public"
},
"hashtag_feeds" => %{
"local" => "authenticated",
"remote" => "public"
},
"trending_link_feeds" => %{
"local" => "disabled",
"remote" => "disabled"
}
}
end
end

View file

@ -280,35 +280,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
end
end
describe "account endorsements" do
test "returns a list of pinned accounts", %{conn: conn} do
%{id: id1} = user1 = insert(:user)
%{id: id2} = user2 = insert(:user)
%{id: id3} = user3 = insert(:user)
CommonAPI.follow(user2, user1)
CommonAPI.follow(user3, user1)
User.endorse(user1, user2)
User.endorse(user1, user3)
response =
conn
|> get("/api/v1/pleroma/accounts/#{id1}/endorsements")
|> json_response_and_validate_schema(200)
assert length(response) == 2
assert Enum.any?(response, fn user -> user["id"] == id2 end)
assert Enum.any?(response, fn user -> user["id"] == id3 end)
end
test "returns 404 error when specified user is not exist", %{conn: conn} do
conn = get(conn, "/api/v1/pleroma/accounts/test/endorsements")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "birthday reminders" do
test "returns a list of friends having birthday on specified day" do
%{user: user, conn: conn} = oauth_access(["read:accounts"])