forked from AkkomaGang/akkoma
DELETE /api/pleroma/admin/users now accepts nicknames array
This commit is contained in:
commit
da0e4879bc
34 changed files with 431 additions and 112 deletions
|
@ -70,7 +70,7 @@ docs-deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: alpine:latest
|
image: alpine:latest
|
||||||
only:
|
only:
|
||||||
- master@pleroma/pleroma
|
- stable@pleroma/pleroma
|
||||||
- develop@pleroma/pleroma
|
- develop@pleroma/pleroma
|
||||||
before_script:
|
before_script:
|
||||||
- apk add curl
|
- apk add curl
|
||||||
|
@ -127,9 +127,10 @@ amd64:
|
||||||
# TODO: Replace with upstream image when 1.9.0 comes out
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
image: rinpatch/elixir:1.9.0-rc.0
|
image: rinpatch/elixir:1.9.0-rc.0
|
||||||
only: &release-only
|
only: &release-only
|
||||||
- master@pleroma/pleroma
|
- stable@pleroma/pleroma
|
||||||
- develop@pleroma/pleroma
|
- develop@pleroma/pleroma
|
||||||
- /^maint/.*$/@pleroma/pleroma
|
- /^maint/.*$/@pleroma/pleroma
|
||||||
|
- /^release/.*$/@pleroma/pleroma
|
||||||
artifacts: &release-artifacts
|
artifacts: &release-artifacts
|
||||||
name: "pleroma-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
|
name: "pleroma-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -17,8 +17,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Authentication: Added rate limit for password-authorized actions / login existence checks
|
- Authentication: Added rate limit for password-authorized actions / login existence checks
|
||||||
- Metadata Link: Atom syndication Feed
|
- Metadata Link: Atom syndication Feed
|
||||||
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
||||||
|
- Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
|
||||||
- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
|
- Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
|
||||||
- Admin API: `POST /api/pleroma/admin/users/:nickname/permission_group/:permission_group` / `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST /api/pleroma/admin/users/permission_group/:permission_group` / `DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array)
|
- Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
|
||||||
|
@ -39,6 +40,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Report emails now include functional links to profiles of remote user accounts
|
- Report emails now include functional links to profiles of remote user accounts
|
||||||
|
|
||||||
## [1.1.0] - 2019-??-??
|
## [1.1.0] - 2019-??-??
|
||||||
|
**Breaking:** The stable branch has been changed from `master` to `stable`, `master` now points to `release/1.0`
|
||||||
### Security
|
### Security
|
||||||
- Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
|
- Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ Authentication is required and the user must be an admin.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/pleroma/admin/users`
|
## DEPRECATED `DELETE /api/pleroma/admin/users`
|
||||||
|
|
||||||
### Remove a user
|
### Remove a user
|
||||||
|
|
||||||
|
@ -56,6 +56,15 @@ Authentication is required and the user must be an admin.
|
||||||
- `nickname`
|
- `nickname`
|
||||||
- Response: User’s nickname
|
- Response: User’s nickname
|
||||||
|
|
||||||
|
## `DELETE /api/pleroma/admin/users`
|
||||||
|
|
||||||
|
### Remove a user
|
||||||
|
|
||||||
|
- Method `DELETE`
|
||||||
|
- Params:
|
||||||
|
- `nicknames`
|
||||||
|
- Response: Array of user nicknames
|
||||||
|
|
||||||
### Create a user
|
### Create a user
|
||||||
|
|
||||||
- Method: `POST`
|
- Method: `POST`
|
||||||
|
|
|
@ -13,6 +13,7 @@ Some apps operate under the assumption that no more than 4 attachments can be re
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||||
|
|
||||||
## Statuses
|
## Statuses
|
||||||
|
|
||||||
|
@ -84,6 +85,12 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `is_seen`: true if the notification was read by the user
|
- `is_seen`: true if the notification was read by the user
|
||||||
|
|
||||||
|
## GET `/api/v1/notifications`
|
||||||
|
|
||||||
|
Accepts additional parameters:
|
||||||
|
|
||||||
|
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||||
|
|
||||||
## POST `/api/v1/statuses`
|
## POST `/api/v1/statuses`
|
||||||
|
|
||||||
Additional parameters can be added to the JSON body/Form data:
|
Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
|
@ -91,7 +91,7 @@ sudo adduser -S -s /bin/false -h /opt/pleroma -H -G pleroma pleroma
|
||||||
```shell
|
```shell
|
||||||
sudo mkdir -p /opt/pleroma
|
sudo mkdir -p /opt/pleroma
|
||||||
sudo chown -R pleroma:pleroma /opt/pleroma
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
* Change to the new directory:
|
* Change to the new directory:
|
||||||
|
|
|
@ -66,7 +66,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
```shell
|
```shell
|
||||||
sudo mkdir -p /opt/pleroma
|
sudo mkdir -p /opt/pleroma
|
||||||
sudo chown -R pleroma:pleroma /opt/pleroma
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
* Change to the new directory:
|
* Change to the new directory:
|
||||||
|
|
|
@ -143,7 +143,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
```shell
|
```shell
|
||||||
sudo mkdir -p /opt/pleroma
|
sudo mkdir -p /opt/pleroma
|
||||||
sudo chown -R pleroma:pleroma /opt/pleroma
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
* Change to the new directory:
|
* Change to the new directory:
|
||||||
|
|
|
@ -68,7 +68,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
```shell
|
```shell
|
||||||
sudo mkdir -p /opt/pleroma
|
sudo mkdir -p /opt/pleroma
|
||||||
sudo chown -R pleroma:pleroma /opt/pleroma
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
* Change to the new directory:
|
* Change to the new directory:
|
||||||
|
|
|
@ -68,7 +68,7 @@ sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
```
|
```
|
||||||
sudo mkdir -p /opt/pleroma
|
sudo mkdir -p /opt/pleroma
|
||||||
sudo chown -R pleroma:pleroma /opt/pleroma
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
sudo -Hu pleroma git clone -b master https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
* 新しいディレクトリに移動します。
|
* 新しいディレクトリに移動します。
|
||||||
|
|
|
@ -106,7 +106,7 @@ It is highly recommended you use your own fork for the `https://path/to/repo` pa
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pleroma$ cd ~
|
pleroma$ cd ~
|
||||||
pleroma$ git clone -b master https://path/to/repo
|
pleroma$ git clone -b stable https://path/to/repo
|
||||||
```
|
```
|
||||||
|
|
||||||
* Change to the new directory:
|
* Change to the new directory:
|
||||||
|
|
|
@ -96,9 +96,9 @@ rm -r ~pleroma/*
|
||||||
export FLAVOUR="arm64-musl"
|
export FLAVOUR="arm64-musl"
|
||||||
|
|
||||||
# Clone the release build into a temporary directory and unpack it
|
# Clone the release build into a temporary directory and unpack it
|
||||||
# Replace `master` with `develop` if you want to run the develop branch
|
# Replace `stable` with `unstable` if you want to run the unstable branch
|
||||||
su pleroma -s $SHELL -lc "
|
su pleroma -s $SHELL -lc "
|
||||||
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/master/download?job=$FLAVOUR' -o /tmp/pleroma.zip
|
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
|
||||||
unzip /tmp/pleroma.zip -d /tmp/
|
unzip /tmp/pleroma.zip -d /tmp/
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ Clone the repository:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cd /home/pleroma
|
$ cd /home/pleroma
|
||||||
$ git clone -b master https://git.pleroma.social/pleroma/pleroma.git
|
$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Configure Pleroma. Note that you need a domain name at this point:
|
Configure Pleroma. Note that you need a domain name at this point:
|
||||||
|
|
|
@ -29,7 +29,7 @@ This creates a "pleroma" login class and sets higher values than default for dat
|
||||||
Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
|
Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
|
||||||
|
|
||||||
#### Clone pleroma's directory
|
#### Clone pleroma's directory
|
||||||
Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b master https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
|
Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
|
||||||
|
|
||||||
#### Postgresql
|
#### Postgresql
|
||||||
Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:
|
Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:
|
||||||
|
|
|
@ -44,7 +44,7 @@ Vaihda pleroma-käyttäjään ja mene kotihakemistoosi:
|
||||||
|
|
||||||
Lataa pleroman lähdekoodi:
|
Lataa pleroman lähdekoodi:
|
||||||
|
|
||||||
`$ git clone -b master https://git.pleroma.social/pleroma/pleroma.git`
|
`$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git`
|
||||||
|
|
||||||
`$ cd pleroma`
|
`$ cd pleroma`
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ export FLAVOUR="arm64-musl"
|
||||||
|
|
||||||
# Clone the release build into a temporary directory and unpack it
|
# Clone the release build into a temporary directory and unpack it
|
||||||
su pleroma -s $SHELL -lc "
|
su pleroma -s $SHELL -lc "
|
||||||
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/master/download?job=$FLAVOUR' -o /tmp/pleroma.zip
|
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
|
||||||
unzip /tmp/pleroma.zip -d /tmp/
|
unzip /tmp/pleroma.zip -d /tmp/
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|
|
@ -352,10 +352,10 @@ def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
"action" => "delete",
|
"action" => "delete",
|
||||||
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
"subject" => subjects
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} deleted user @#{subject_nickname}"
|
"@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Notification do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
require Logger
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
@ -34,43 +35,92 @@ def changeset(%Notification{} = notification, attrs) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def for_user_query(user, opts \\ []) do
|
def for_user_query(user, opts \\ []) do
|
||||||
query =
|
Notification
|
||||||
Notification
|
|> where(user_id: ^user.id)
|
||||||
|> where(user_id: ^user.id)
|
|> where(
|
||||||
|
[n, a],
|
||||||
|
fragment(
|
||||||
|
"? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
|
||||||
|
a.actor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> join(:inner, [n], activity in assoc(n, :activity))
|
||||||
|
|> join(:left, [n, a], object in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
object.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([n, a, o], activity: {a, object: o})
|
||||||
|
|> exclude_muted(user, opts)
|
||||||
|
|> exclude_visibility(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_muted(query, _, %{with_muted: true}) do
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_muted(query, user, _opts) do
|
||||||
|
query
|
||||||
|
|> where([n, a], a.actor not in ^user.info.muted_notifications)
|
||||||
|
|> where([n, a], a.actor not in ^user.info.blocks)
|
||||||
|
|> where(
|
||||||
|
[n, a],
|
||||||
|
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
|
||||||
|
)
|
||||||
|
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
||||||
|
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
||||||
|
)
|
||||||
|
|> where([n, a, o, tm], is_nil(tm.user_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
@valid_visibilities ~w[direct unlisted public private]
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
|
when is_list(visibility) do
|
||||||
|
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||||
|
query
|
||||||
|> where(
|
|> where(
|
||||||
[n, a],
|
[n, a],
|
||||||
fragment(
|
not fragment(
|
||||||
"? not in (SELECT ap_id FROM users WHERE info->'deactivated' @> 'true')",
|
"activity_visibility(?, ?, ?) = ANY (?)",
|
||||||
a.actor
|
a.actor,
|
||||||
|
a.recipients,
|
||||||
|
a.data,
|
||||||
|
^visibility
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> join(:inner, [n], activity in assoc(n, :activity))
|
|
||||||
|> join(:left, [n, a], object in Object,
|
|
||||||
on:
|
|
||||||
fragment(
|
|
||||||
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
|
||||||
object.data,
|
|
||||||
a.data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|> preload([n, a, o], activity: {a, object: o})
|
|
||||||
|
|
||||||
if opts[:with_muted] do
|
|
||||||
query
|
|
||||||
else
|
else
|
||||||
where(query, [n, a], a.actor not in ^user.info.muted_notifications)
|
Logger.error("Could not exclude visibility to #{visibility}")
|
||||||
|> where([n, a], a.actor not in ^user.info.blocks)
|
query
|
||||||
|> where(
|
|
||||||
[n, a],
|
|
||||||
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.info.domain_blocks
|
|
||||||
)
|
|
||||||
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
|
||||||
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
|
||||||
)
|
|
||||||
|> where([n, a, o, tm], is_nil(tm.user_id))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
|
when visibility in @valid_visibilities do
|
||||||
|
query
|
||||||
|
|> where(
|
||||||
|
[n, a],
|
||||||
|
not fragment(
|
||||||
|
"activity_visibility(?, ?, ?) = (?)",
|
||||||
|
a.actor,
|
||||||
|
a.recipients,
|
||||||
|
a.data,
|
||||||
|
^visibility
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||||
|
when visibility not in @valid_visibilities do
|
||||||
|
Logger.error("Could not exclude visibility to #{visibility}")
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, _visibility), do: query
|
||||||
|
|
||||||
def for_user(user, opts \\ %{}) do
|
def for_user(user, opts \\ %{}) do
|
||||||
user
|
user
|
||||||
|> for_user_query(opts)
|
|> for_user_query(opts)
|
||||||
|
|
|
@ -401,11 +401,9 @@ defp increase_read_duration(_) do
|
||||||
|
|
||||||
defp client, do: Pleroma.ReverseProxy.Client
|
defp client, do: Pleroma.ReverseProxy.Client
|
||||||
|
|
||||||
defp track_failed_url(url, code, opts) do
|
defp track_failed_url(url, error, opts) do
|
||||||
code = to_string(code)
|
|
||||||
|
|
||||||
ttl =
|
ttl =
|
||||||
if code in ["403", "404"] or String.starts_with?(code, "5") do
|
unless error in [:body_too_large, 400, 204] do
|
||||||
Keyword.get(opts, :failed_request_ttl, @failed_request_ttl)
|
Keyword.get(opts, :failed_request_ttl, @failed_request_ttl)
|
||||||
else
|
else
|
||||||
nil
|
nil
|
||||||
|
|
|
@ -1080,6 +1080,10 @@ def update_notification_settings(%User{} = user, settings \\ %{}) do
|
||||||
update_info(user, &User.Info.update_notification_settings(&1, settings))
|
update_info(user, &User.Info.update_notification_settings(&1, settings))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete(users) when is_list(users) do
|
||||||
|
for user <- users, do: delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
def delete(%User{} = user) do
|
def delete(%User{} = user) do
|
||||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||||
end
|
end
|
||||||
|
|
|
@ -269,22 +269,21 @@ def listen(%{to: to, actor: actor, context: context, object: object} = params) d
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def accept(%{to: to, actor: actor, object: object} = params) do
|
def accept(params) do
|
||||||
# only accept false as false value
|
accept_or_reject("Accept", params)
|
||||||
local = !(params[:local] == false)
|
|
||||||
|
|
||||||
with data <- %{"to" => to, "type" => "Accept", "actor" => actor.ap_id, "object" => object},
|
|
||||||
{:ok, activity} <- insert(data, local),
|
|
||||||
:ok <- maybe_federate(activity) do
|
|
||||||
{:ok, activity}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject(%{to: to, actor: actor, object: object} = params) do
|
def reject(params) do
|
||||||
# only accept false as false value
|
accept_or_reject("Reject", params)
|
||||||
local = !(params[:local] == false)
|
end
|
||||||
|
|
||||||
with data <- %{"to" => to, "type" => "Reject", "actor" => actor.ap_id, "object" => object},
|
def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
||||||
|
local = Map.get(params, :local, true)
|
||||||
|
activity_id = Map.get(params, :activity_id, nil)
|
||||||
|
|
||||||
|
with data <-
|
||||||
|
%{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
|
||||||
|
|> Utils.maybe_put("id", activity_id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
@ -409,18 +408,24 @@ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
|
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
|
||||||
|
local = Keyword.get(options, :local, true)
|
||||||
|
activity_id = Keyword.get(options, :activity_id, nil)
|
||||||
|
actor = Keyword.get(options, :actor, actor)
|
||||||
|
|
||||||
user = User.get_cached_by_ap_id(actor)
|
user = User.get_cached_by_ap_id(actor)
|
||||||
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
|
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
|
||||||
|
|
||||||
with {:ok, object, activity} <- Object.delete(object),
|
with {:ok, object, activity} <- Object.delete(object),
|
||||||
data <- %{
|
data <-
|
||||||
"type" => "Delete",
|
%{
|
||||||
"actor" => actor,
|
"type" => "Delete",
|
||||||
"object" => id,
|
"actor" => actor,
|
||||||
"to" => to,
|
"object" => id,
|
||||||
"deleted_activity_id" => activity && activity.id
|
"to" => to,
|
||||||
},
|
"deleted_activity_id" => activity && activity.id
|
||||||
|
}
|
||||||
|
|> maybe_put("id", activity_id),
|
||||||
{:ok, activity} <- insert(data, local, false),
|
{:ok, activity} <- insert(data, local, false),
|
||||||
stream_out_participations(object, user),
|
stream_out_participations(object, user),
|
||||||
_ <- decrease_replies_count_if_reply(object),
|
_ <- decrease_replies_count_if_reply(object),
|
||||||
|
@ -591,6 +596,49 @@ defp restrict_visibility(_query, %{visibility: visibility})
|
||||||
|
|
||||||
defp restrict_visibility(query, _visibility), do: query
|
defp restrict_visibility(query, _visibility), do: query
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||||
|
when is_list(visibility) do
|
||||||
|
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||||
|
from(
|
||||||
|
a in query,
|
||||||
|
where:
|
||||||
|
not fragment(
|
||||||
|
"activity_visibility(?, ?, ?) = ANY (?)",
|
||||||
|
a.actor,
|
||||||
|
a.recipients,
|
||||||
|
a.data,
|
||||||
|
^visibility
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Logger.error("Could not exclude visibility to #{visibility}")
|
||||||
|
query
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||||
|
when visibility in @valid_visibilities do
|
||||||
|
from(
|
||||||
|
a in query,
|
||||||
|
where:
|
||||||
|
not fragment(
|
||||||
|
"activity_visibility(?, ?, ?) = ?",
|
||||||
|
a.actor,
|
||||||
|
a.recipients,
|
||||||
|
a.data,
|
||||||
|
^visibility
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||||
|
when visibility not in @valid_visibilities do
|
||||||
|
Logger.error("Could not exclude visibility to #{visibility}")
|
||||||
|
query
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_visibility(query, _visibility), do: query
|
||||||
|
|
||||||
defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
|
defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
|
||||||
do: query
|
do: query
|
||||||
|
|
||||||
|
@ -955,6 +1003,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_muted_reblogs(opts)
|
|> restrict_muted_reblogs(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|
|> exclude_visibility(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
||||||
|
|
|
@ -514,7 +514,7 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
|
%{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
) do
|
) do
|
||||||
with actor <- Containment.get_actor(data),
|
with actor <- Containment.get_actor(data),
|
||||||
|
@ -528,7 +528,8 @@ def handle_incoming(
|
||||||
type: "Accept",
|
type: "Accept",
|
||||||
actor: followed,
|
actor: followed,
|
||||||
object: follow_activity.data["id"],
|
object: follow_activity.data["id"],
|
||||||
local: false
|
local: false,
|
||||||
|
activity_id: id
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
_e -> :error
|
_e -> :error
|
||||||
|
@ -536,7 +537,7 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data,
|
%{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
) do
|
) do
|
||||||
with actor <- Containment.get_actor(data),
|
with actor <- Containment.get_actor(data),
|
||||||
|
@ -550,7 +551,8 @@ def handle_incoming(
|
||||||
type: "Reject",
|
type: "Reject",
|
||||||
actor: followed,
|
actor: followed,
|
||||||
object: follow_activity.data["id"],
|
object: follow_activity.data["id"],
|
||||||
local: false
|
local: false,
|
||||||
|
activity_id: id
|
||||||
}) do
|
}) do
|
||||||
User.unfollow(follower, followed)
|
User.unfollow(follower, followed)
|
||||||
|
|
||||||
|
@ -637,7 +639,7 @@ def handle_incoming(
|
||||||
# an error or a tombstone. This would allow us to verify that a deletion actually took
|
# an error or a tombstone. This would allow us to verify that a deletion actually took
|
||||||
# place.
|
# place.
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = data,
|
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data,
|
||||||
_options
|
_options
|
||||||
) do
|
) do
|
||||||
object_id = Utils.get_ap_id(object_id)
|
object_id = Utils.get_ap_id(object_id)
|
||||||
|
@ -646,7 +648,8 @@ def handle_incoming(
|
||||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
|
||||||
{:ok, object} <- get_obj_helper(object_id),
|
{:ok, object} <- get_obj_helper(object_id),
|
||||||
:ok <- Containment.contain_origin(actor.ap_id, object.data),
|
:ok <- Containment.contain_origin(actor.ap_id, object.data),
|
||||||
{:ok, activity} <- ActivityPub.delete(object, false) do
|
{:ok, activity} <-
|
||||||
|
ActivityPub.delete(object, local: false, activity_id: id, actor: actor.ap_id) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
nil ->
|
nil ->
|
||||||
|
|
|
@ -100,7 +100,7 @@ def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: admin,
|
actor: admin,
|
||||||
subject: user,
|
subject: [user],
|
||||||
action: "delete"
|
action: "delete"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -108,6 +108,20 @@ def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|> json(nickname)
|
|> json(nickname)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
User.delete(users)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "delete"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(nicknames)
|
||||||
|
end
|
||||||
|
|
||||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
"follower" => follower_nick,
|
"follower" => follower_nick,
|
||||||
"followed" => followed_nick
|
"followed" => followed_nick
|
||||||
|
|
|
@ -71,6 +71,7 @@ def get_scheduled_activities(user, params \\ %{}) do
|
||||||
defp cast_params(params) do
|
defp cast_params(params) do
|
||||||
param_types = %{
|
param_types = %{
|
||||||
exclude_types: {:array, :string},
|
exclude_types: {:array, :string},
|
||||||
|
exclude_visibilities: {:array, :string},
|
||||||
reblogs: :boolean,
|
reblogs: :boolean,
|
||||||
with_muted: :boolean
|
with_muted: :boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.OStatus.DeleteHandler do
|
||||||
def handle_delete(entry, _doc \\ nil) do
|
def handle_delete(entry, _doc \\ nil) do
|
||||||
with id <- XML.string_from_xpath("//id", entry),
|
with id <- XML.string_from_xpath("//id", entry),
|
||||||
%Object{} = object <- Object.normalize(id),
|
%Object{} = object <- Object.normalize(id),
|
||||||
{:ok, delete} <- ActivityPub.delete(object, false) do
|
{:ok, delete} <- ActivityPub.delete(object, local: false) do
|
||||||
delete
|
delete
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
5
mix.exs
5
mix.exs
|
@ -220,7 +220,10 @@ defp version(version) do
|
||||||
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||||
branch_name <- String.trim(branch_name),
|
branch_name <- String.trim(branch_name),
|
||||||
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
||||||
true <- branch_name not in ["master", "HEAD"] do
|
true <-
|
||||||
|
!Enum.any?(["master", "HEAD", "release/", "stable"], fn name ->
|
||||||
|
String.starts_with?(name, branch_name)
|
||||||
|
end) do
|
||||||
branch_name =
|
branch_name =
|
||||||
branch_name
|
branch_name
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|
|
|
@ -35,11 +35,11 @@ detect_branch() {
|
||||||
if [ "$branch" = "develop" ]; then
|
if [ "$branch" = "develop" ]; then
|
||||||
echo "develop"
|
echo "develop"
|
||||||
elif [ "$branch" = "" ]; then
|
elif [ "$branch" = "" ]; then
|
||||||
echo "master"
|
echo "stable"
|
||||||
else
|
else
|
||||||
# Note: branch name in version is of SemVer format and may only contain [0-9a-zA-Z-] symbols —
|
# Note: branch name in version is of SemVer format and may only contain [0-9a-zA-Z-] symbols —
|
||||||
# if supporting releases for more branches, need to ensure they contain only these symbols.
|
# if supporting releases for more branches, need to ensure they contain only these symbols.
|
||||||
echo "Releases are built only for master and develop branches" >&2
|
echo "Can't detect the branch automatically, please specify it by using the --branch option." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,13 @@ test "logging user deletion by moderator", %{moderator: moderator, subject1: sub
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
actor: moderator,
|
actor: moderator,
|
||||||
subject: subject1,
|
subject: [subject1],
|
||||||
action: "delete"
|
action: "delete"
|
||||||
})
|
})
|
||||||
|
|
||||||
log = Repo.one(ModerationLog)
|
log = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}"
|
assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "logging user creation by moderator", %{
|
test "logging user creation by moderator", %{
|
||||||
|
|
|
@ -87,6 +87,66 @@ test "it restricts by the appropriate visibility" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "fetching excluded by visibility" do
|
||||||
|
test "it excludes by the appropriate visibility" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
|
||||||
|
|
||||||
|
{:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
|
||||||
|
|
||||||
|
{:ok, unlisted_activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
|
||||||
|
|
||||||
|
{:ok, private_activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{
|
||||||
|
"exclude_visibilities" => "direct",
|
||||||
|
"actor_id" => user.ap_id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert public_activity in activities
|
||||||
|
assert unlisted_activity in activities
|
||||||
|
assert private_activity in activities
|
||||||
|
refute direct_activity in activities
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{
|
||||||
|
"exclude_visibilities" => "unlisted",
|
||||||
|
"actor_id" => user.ap_id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert public_activity in activities
|
||||||
|
refute unlisted_activity in activities
|
||||||
|
assert private_activity in activities
|
||||||
|
assert direct_activity in activities
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{
|
||||||
|
"exclude_visibilities" => "private",
|
||||||
|
"actor_id" => user.ap_id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert public_activity in activities
|
||||||
|
assert unlisted_activity in activities
|
||||||
|
refute private_activity in activities
|
||||||
|
assert direct_activity in activities
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_activities([], %{
|
||||||
|
"exclude_visibilities" => "public",
|
||||||
|
"actor_id" => user.ap_id
|
||||||
|
})
|
||||||
|
|
||||||
|
refute public_activity in activities
|
||||||
|
assert unlisted_activity in activities
|
||||||
|
assert private_activity in activities
|
||||||
|
assert direct_activity in activities
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "building a user from his ap id" do
|
describe "building a user from his ap id" do
|
||||||
test "it returns a user" do
|
test "it returns a user" do
|
||||||
user_id = "http://mastodon.example.org/users/admin"
|
user_id = "http://mastodon.example.org/users/admin"
|
||||||
|
|
|
@ -682,6 +682,7 @@ test "it works for incoming update activities which lock the account" do
|
||||||
|
|
||||||
test "it works for incoming deletes" do
|
test "it works for incoming deletes" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
deleting_user = insert(:user)
|
||||||
|
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-delete.json")
|
File.read!("test/fixtures/mastodon-delete.json")
|
||||||
|
@ -694,11 +695,14 @@ test "it works for incoming deletes" do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.put("actor", activity.data["actor"])
|
|> Map.put("actor", deleting_user.ap_id)
|
||||||
|
|
||||||
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
|
{:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} =
|
||||||
|
Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert id == data["id"]
|
||||||
refute Activity.get_by_id(activity.id)
|
refute Activity.get_by_id(activity.id)
|
||||||
|
assert actor == deleting_user.ap_id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it fails for incoming deletes with spoofed origin" do
|
test "it fails for incoming deletes with spoofed origin" do
|
||||||
|
@ -905,6 +909,8 @@ test "it works for incoming accepts which were pre-accepted" do
|
||||||
|
|
||||||
assert activity.data["object"] == follow_activity.data["id"]
|
assert activity.data["object"] == follow_activity.data["id"]
|
||||||
|
|
||||||
|
assert activity.data["id"] == accept_data["id"]
|
||||||
|
|
||||||
follower = User.get_cached_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, followed) == true
|
assert User.following?(follower, followed) == true
|
||||||
|
@ -1009,6 +1015,7 @@ test "it works for incoming rejects which are orphaned" do
|
||||||
|
|
||||||
{:ok, activity} = Transmogrifier.handle_incoming(reject_data)
|
{:ok, activity} = Transmogrifier.handle_incoming(reject_data)
|
||||||
refute activity.local
|
refute activity.local
|
||||||
|
assert activity.data["id"] == reject_data["id"]
|
||||||
|
|
||||||
follower = User.get_cached_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "/api/pleroma/admin/users" do
|
describe "DELETE /api/pleroma/admin/users" do
|
||||||
test "Delete" do
|
test "single user" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -30,15 +30,36 @@ test "Delete" do
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
assert log_entry.data["subject"]["nickname"] == user.nickname
|
|
||||||
assert log_entry.data["action"] == "delete"
|
|
||||||
|
|
||||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
"@#{admin.nickname} deleted user @#{user.nickname}"
|
"@#{admin.nickname} deleted users: @#{user.nickname}"
|
||||||
|
|
||||||
assert json_response(conn, 200) == user.nickname
|
assert json_response(conn, 200) == user.nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "multiple users" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/users", %{
|
||||||
|
nicknames: [user_one.nickname, user_two.nickname]
|
||||||
|
})
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert response -- [user_one.nickname, user_two.nickname] == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/users" do
|
||||||
test "Create" do
|
test "Create" do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
|
|
@ -237,6 +237,20 @@ test "filters user's statuses by a hashtag", %{conn: conn} do
|
||||||
assert [%{"id" => id}] = json_response(conn, 200)
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
assert id == to_string(post.id)
|
assert id == to_string(post.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "the user views their own timelines and excludes direct messages", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
|
||||||
|
{:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_visibilities" => ["direct"]})
|
||||||
|
|
||||||
|
assert [%{"id" => id}] = json_response(conn, 200)
|
||||||
|
assert id == to_string(public_activity.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "followers" do
|
describe "followers" do
|
||||||
|
|
|
@ -137,6 +137,57 @@ test "paginates notifications using min_id, since_id, max_id, and limit", %{conn
|
||||||
assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
|
assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "filters notifications using exclude_visibilities", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, public_activity} =
|
||||||
|
CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
|
||||||
|
|
||||||
|
{:ok, direct_activity} =
|
||||||
|
CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
|
||||||
|
|
||||||
|
{:ok, unlisted_activity} =
|
||||||
|
CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
|
||||||
|
|
||||||
|
{:ok, private_activity} =
|
||||||
|
CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
|
||||||
|
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
|
|
||||||
|
conn_res =
|
||||||
|
get(conn, "/api/v1/notifications", %{
|
||||||
|
exclude_visibilities: ["public", "unlisted", "private"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
|
||||||
|
assert id == direct_activity.id
|
||||||
|
|
||||||
|
conn_res =
|
||||||
|
get(conn, "/api/v1/notifications", %{
|
||||||
|
exclude_visibilities: ["public", "unlisted", "direct"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
|
||||||
|
assert id == private_activity.id
|
||||||
|
|
||||||
|
conn_res =
|
||||||
|
get(conn, "/api/v1/notifications", %{
|
||||||
|
exclude_visibilities: ["public", "private", "direct"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
|
||||||
|
assert id == unlisted_activity.id
|
||||||
|
|
||||||
|
conn_res =
|
||||||
|
get(conn, "/api/v1/notifications", %{
|
||||||
|
exclude_visibilities: ["unlisted", "private", "direct"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
|
||||||
|
assert id == public_activity.id
|
||||||
|
end
|
||||||
|
|
||||||
test "filters notifications using exclude_types", %{conn: conn} do
|
test "filters notifications using exclude_types", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
|
@ -40,9 +40,9 @@ test "it returns empty result if user or status search return undefined error",
|
||||||
test "search", %{conn: conn} do
|
test "search", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu 天子"})
|
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private 天子"})
|
||||||
|
|
||||||
{:ok, _activity} =
|
{:ok, _activity} =
|
||||||
CommonAPI.post(user, %{
|
CommonAPI.post(user, %{
|
||||||
|
@ -70,8 +70,8 @@ test "search", %{conn: conn} do
|
||||||
get(conn, "/api/v2/search", %{"q" => "天子"})
|
get(conn, "/api/v2/search", %{"q" => "天子"})
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
[account] == results["accounts"]
|
[status] = results["statuses"]
|
||||||
assert account["id"] == to_string(user_three.id)
|
assert status["id"] == to_string(activity.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,27 +20,52 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the home timeline", %{conn: conn} do
|
describe "home" do
|
||||||
user = insert(:user)
|
test "the home timeline", %{conn: conn} do
|
||||||
following = insert(:user)
|
user = insert(:user)
|
||||||
|
following = insert(:user)
|
||||||
|
|
||||||
{:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
|
{:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/timelines/home")
|
|> get("/api/v1/timelines/home")
|
||||||
|
|
||||||
assert Enum.empty?(json_response(conn, :ok))
|
assert Enum.empty?(json_response(conn, :ok))
|
||||||
|
|
||||||
{:ok, user} = User.follow(user, following)
|
{:ok, user} = User.follow(user, following)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/timelines/home")
|
|> get("/api/v1/timelines/home")
|
||||||
|
|
||||||
assert [%{"content" => "test"}] = json_response(conn, :ok)
|
assert [%{"content" => "test"}] = json_response(conn, :ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "the home timeline when the direct messages are excluded", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
|
||||||
|
{:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
|
||||||
|
|
||||||
|
{:ok, unlisted_activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
|
||||||
|
|
||||||
|
{:ok, private_activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]})
|
||||||
|
|
||||||
|
assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"])
|
||||||
|
assert public_activity.id in status_ids
|
||||||
|
assert unlisted_activity.id in status_ids
|
||||||
|
assert private_activity.id in status_ids
|
||||||
|
refute direct_activity.id in status_ids
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "public" do
|
describe "public" do
|
||||||
|
|
Loading…
Reference in a new issue