forked from AkkomaGang/akkoma
Merge remote-tracking branch 'origin/develop' into manifest
This commit is contained in:
commit
e4f9cb1c1b
45 changed files with 838 additions and 66 deletions
|
@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Allow users to remove their emails if instance does not need email to register
|
- Allow users to remove their emails if instance does not need email to register
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||||
|
|
|
@ -12,7 +12,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
|
||||||
mkdir release &&\
|
mkdir release &&\
|
||||||
mix release --path release
|
mix release --path release
|
||||||
|
|
||||||
FROM alpine:3.11
|
FROM alpine:3.14
|
||||||
|
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
ARG VCS_REF
|
ARG VCS_REF
|
||||||
|
@ -31,8 +31,7 @@ LABEL maintainer="ops@pleroma.social" \
|
||||||
ARG HOME=/opt/pleroma
|
ARG HOME=/opt/pleroma
|
||||||
ARG DATA=/var/lib/pleroma
|
ARG DATA=/var/lib/pleroma
|
||||||
|
|
||||||
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
RUN apk update &&\
|
||||||
apk update &&\
|
|
||||||
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
||||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||||
mkdir -p ${DATA}/uploads &&\
|
mkdir -p ${DATA}/uploads &&\
|
||||||
|
|
|
@ -394,7 +394,7 @@ defp get_actor(group, users), do: Enum.random(users[group])
|
||||||
|
|
||||||
defp other_data(actor, content) do
|
defp other_data(actor, content) do
|
||||||
%{host: host} = URI.parse(actor.ap_id)
|
%{host: host} = URI.parse(actor.ap_id)
|
||||||
datetime = DateTime.utc_now()
|
datetime = DateTime.utc_now() |> to_string()
|
||||||
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||||
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||||
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||||
|
|
|
@ -99,15 +99,16 @@ defp hashtag_fetching(params, user, local_only) do
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase(&1))
|
||||||
|
|
||||||
_activities =
|
_activities =
|
||||||
params
|
%{
|
||||||
|> Map.put(:type, "Create")
|
type: "Create",
|
||||||
|> Map.put(:local_only, local_only)
|
local_only: local_only,
|
||||||
|> Map.put(:blocking_user, user)
|
blocking_user: user,
|
||||||
|> Map.put(:muting_user, user)
|
muting_user: user,
|
||||||
|> Map.put(:user, user)
|
user: user,
|
||||||
|> Map.put(:tag, tags)
|
tag: tags,
|
||||||
|> Map.put(:tag_all, tag_all)
|
tag_all: tag_all,
|
||||||
|> Map.put(:tag_reject, tag_reject)
|
tag_reject: tag_reject,
|
||||||
|
}
|
||||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,14 +17,14 @@ def run(_args) do
|
||||||
# Let the user make 100 posts
|
# Let the user make 100 posts
|
||||||
|
|
||||||
1..100
|
1..100
|
||||||
|> Enum.each(fn i -> CommonAPI.post(user, %{"status" => to_string(i)}) end)
|
|> Enum.each(fn i -> CommonAPI.post(user, %{status: to_string(i)}) end)
|
||||||
|
|
||||||
# Let 10 random users post
|
# Let 10 random users post
|
||||||
posts =
|
posts =
|
||||||
users
|
users
|
||||||
|> Enum.take_random(10)
|
|> Enum.take_random(10)
|
||||||
|> Enum.map(fn {:ok, random_user} ->
|
|> Enum.map(fn {:ok, random_user} ->
|
||||||
{:ok, activity} = CommonAPI.post(random_user, %{"status" => "."})
|
{:ok, activity} = CommonAPI.post(random_user, %{status: "."})
|
||||||
activity
|
activity
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ def run(_args) do
|
||||||
|> Conn.assign(:user, reading_user)
|
|> Conn.assign(:user, reading_user)
|
||||||
|> Conn.assign(:skip_link_headers, true)
|
|> Conn.assign(:skip_link_headers, true)
|
||||||
|
|
||||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
inputs: %{"user" => user, "no user" => nil},
|
inputs: %{"user" => user, "no user" => nil},
|
||||||
|
@ -50,7 +50,7 @@ def run(_args) do
|
||||||
)
|
)
|
||||||
|
|
||||||
users
|
users
|
||||||
|> Enum.each(fn {:ok, follower, user} -> Pleroma.User.follow(follower, user) end)
|
|> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
|
@ -60,7 +60,7 @@ def run(_args) do
|
||||||
|> Conn.assign(:user, reading_user)
|
|> Conn.assign(:user, reading_user)
|
||||||
|> Conn.assign(:skip_link_headers, true)
|
|> Conn.assign(:skip_link_headers, true)
|
||||||
|
|
||||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
inputs: %{"user" => user, "no user" => nil},
|
inputs: %{"user" => user, "no user" => nil},
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
http: [port: 4001],
|
http: [port: 4001],
|
||||||
url: [port: 4001],
|
url: [port: 4001]
|
||||||
server: true
|
|
||||||
|
|
||||||
# Disable captha for tests
|
# Disable captha for tests
|
||||||
config :pleroma, Pleroma.Captcha,
|
config :pleroma, Pleroma.Captcha,
|
||||||
|
@ -44,7 +43,7 @@
|
||||||
pool_size: 10
|
pool_size: 10
|
||||||
|
|
||||||
# Reduce hash rounds for testing
|
# Reduce hash rounds for testing
|
||||||
config :pbkdf2_elixir, rounds: 1
|
config :pleroma, :password, iterations: 1
|
||||||
|
|
||||||
config :tesla, adapter: Tesla.Mock
|
config :tesla, adapter: Tesla.Mock
|
||||||
|
|
||||||
|
|
|
@ -261,6 +261,46 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/v1/pleroma/admin/users/suggest`
|
||||||
|
|
||||||
|
### Suggest a user
|
||||||
|
|
||||||
|
Adds the user(s) to follower recommendations.
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `PATCH /api/v1/pleroma/admin/users/unsuggest`
|
||||||
|
|
||||||
|
### Unsuggest a user
|
||||||
|
|
||||||
|
Removes the user(s) from follower recommendations.
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`: nicknames array
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
// user object
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
|
## `GET /api/v1/pleroma/admin/users/:nickname_or_id`
|
||||||
|
|
||||||
### Retrive the details of a user
|
### Retrive the details of a user
|
||||||
|
|
|
@ -362,11 +362,9 @@ def following_requests_for_actor(%User{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def restrict_deactivated_users(query) do
|
def restrict_deactivated_users(query) do
|
||||||
deactivated_users =
|
deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||||
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
mute: 2,
|
mute: 2,
|
||||||
reblog_mute: 3,
|
reblog_mute: 3,
|
||||||
notification_mute: 4,
|
notification_mute: 4,
|
||||||
inverse_subscription: 5
|
inverse_subscription: 5,
|
||||||
|
suggestion_dismiss: 6
|
||||||
)
|
)
|
||||||
|
|
||||||
defenum(Pleroma.FollowingRelationship.State,
|
defenum(Pleroma.FollowingRelationship.State,
|
||||||
|
|
|
@ -103,6 +103,7 @@ defp load_pack(pack_dir, emoji_groups) do
|
||||||
pack_file = Path.join(pack_dir, "pack.json")
|
pack_file = Path.join(pack_dir, "pack.json")
|
||||||
|
|
||||||
if File.exists?(pack_file) do
|
if File.exists?(pack_file) do
|
||||||
|
Logger.info("Loading emoji pack from JSON: #{pack_file}")
|
||||||
contents = Jason.decode!(File.read!(pack_file))
|
contents = Jason.decode!(File.read!(pack_file))
|
||||||
|
|
||||||
contents["files"]
|
contents["files"]
|
||||||
|
@ -115,6 +116,7 @@ defp load_pack(pack_dir, emoji_groups) do
|
||||||
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
||||||
|
|
||||||
if File.exists?(emoji_txt) do
|
if File.exists?(emoji_txt) do
|
||||||
|
Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}")
|
||||||
load_from_file(emoji_txt, emoji_groups)
|
load_from_file(emoji_txt, emoji_groups)
|
||||||
else
|
else
|
||||||
extensions = Config.get([:emoji, :pack_extensions])
|
extensions = Config.get([:emoji, :pack_extensions])
|
||||||
|
|
|
@ -338,6 +338,26 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "add_suggestion",
|
||||||
|
"subject" => users
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "remove_suggestion",
|
||||||
|
"subject" => users
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
|
||||||
|
end
|
||||||
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
|
|
@ -148,6 +148,7 @@ defmodule Pleroma.User do
|
||||||
field(:last_active_at, :naive_datetime)
|
field(:last_active_at, :naive_datetime)
|
||||||
field(:disclose_client, :boolean, default: true)
|
field(:disclose_client, :boolean, default: true)
|
||||||
field(:pinned_objects, :map, default: %{})
|
field(:pinned_objects, :map, default: %{})
|
||||||
|
field(:is_suggested, :boolean, default: false)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
|
@ -1676,6 +1677,22 @@ def confirm(%User{is_confirmed: false} = user) do
|
||||||
|
|
||||||
def confirm(%User{} = user), do: {:ok, user}
|
def confirm(%User{} = user), do: {:ok, user}
|
||||||
|
|
||||||
|
def set_suggestion(users, is_suggested) when is_list(users) do
|
||||||
|
Repo.transaction(fn ->
|
||||||
|
Enum.map(users, fn user ->
|
||||||
|
with {:ok, user} <- set_suggestion(user, is_suggested), do: user
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
|
||||||
|
|
||||||
|
def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
|
||||||
|
user
|
||||||
|
|> change(is_suggested: is_suggested)
|
||||||
|
|> update_and_set_cache()
|
||||||
|
end
|
||||||
|
|
||||||
def update_notification_settings(%User{} = user, settings) do
|
def update_notification_settings(%User{} = user, settings) do
|
||||||
user
|
user
|
||||||
|> cast(%{notification_settings: settings}, [])
|
|> cast(%{notification_settings: settings}, [])
|
||||||
|
@ -2474,8 +2491,8 @@ def update_last_active_at(%__MODULE__{local: true} = user) do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def active_user_count(weeks \\ 4) do
|
def active_user_count(days \\ 30) do
|
||||||
active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks)
|
active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
|
||||||
|
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> where([u], u.last_active_at >= ^active_after)
|
|> where([u], u.last_active_at >= ^active_after)
|
||||||
|
|
|
@ -46,6 +46,7 @@ defmodule Pleroma.User.Query do
|
||||||
unconfirmed: boolean(),
|
unconfirmed: boolean(),
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
|
is_suggested: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
invisible: boolean(),
|
invisible: boolean(),
|
||||||
internal: boolean(),
|
internal: boolean(),
|
||||||
|
@ -167,6 +168,10 @@ defp compose_query({:unconfirmed, _}, query) do
|
||||||
where(query, [u], u.is_confirmed == false)
|
where(query, [u], u.is_confirmed == false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:is_suggested, bool}, query) do
|
||||||
|
where(query, [u], u.is_suggested == ^bool)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:followers, %User{id: id}}, query) do
|
defp compose_query({:followers, %User{id: id}}, query) do
|
||||||
query
|
query
|
||||||
|> where([u], u.id != ^id)
|
|> where([u], u.id != ^id)
|
||||||
|
|
|
@ -68,12 +68,14 @@ def fix_media_type(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_href(href, mediaType) do
|
defp handle_href(href, mediaType, data) do
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
"href" => href,
|
"href" => href,
|
||||||
"type" => "Link",
|
"type" => "Link",
|
||||||
"mediaType" => mediaType
|
"mediaType" => mediaType,
|
||||||
|
"width" => data["width"],
|
||||||
|
"height" => data["height"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -81,10 +83,10 @@ defp handle_href(href, mediaType) do
|
||||||
defp fix_url(data) do
|
defp fix_url(data) do
|
||||||
cond do
|
cond do
|
||||||
is_binary(data["url"]) ->
|
is_binary(data["url"]) ->
|
||||||
Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
|
Map.put(data, "url", handle_href(data["url"], data["mediaType"], data))
|
||||||
|
|
||||||
is_binary(data["href"]) and data["url"] == nil ->
|
is_binary(data["href"]) and data["url"] == nil ->
|
||||||
Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
|
Map.put(data, "url", handle_href(data["href"], data["mediaType"], data))
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
data
|
data
|
||||||
|
|
|
@ -63,8 +63,7 @@ def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = pa
|
||||||
date: date
|
date: date
|
||||||
})
|
})
|
||||||
|
|
||||||
with {:ok, %{status: code}} when code in 200..299 <-
|
with {:ok, %{status: code}} = result when code in 200..299 <-
|
||||||
result =
|
|
||||||
HTTP.post(
|
HTTP.post(
|
||||||
inbox,
|
inbox,
|
||||||
json,
|
json,
|
||||||
|
|
|
@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
||||||
:toggle_activation,
|
:toggle_activation,
|
||||||
:activate,
|
:activate,
|
||||||
:deactivate,
|
:deactivate,
|
||||||
:approve
|
:approve,
|
||||||
|
:suggest,
|
||||||
|
:unsuggest
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -239,6 +241,32 @@ def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = c
|
||||||
render(conn, "index.json", users: updated_users)
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.set_suggestion(users, true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "add_suggestion"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "index.json", users: updated_users)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.set_suggestion(users, false)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "remove_suggestion"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "index.json", users: updated_users)
|
||||||
|
end
|
||||||
|
|
||||||
def index(conn, params) do
|
def index(conn, params) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
filters = maybe_parse_filters(params[:filters])
|
filters = maybe_parse_filters(params[:filters])
|
||||||
|
|
|
@ -80,6 +80,7 @@ def render("show.json", %{user: user}) do
|
||||||
"tags" => user.tags || [],
|
"tags" => user.tags || [],
|
||||||
"is_confirmed" => user.is_confirmed,
|
"is_confirmed" => user.is_confirmed,
|
||||||
"is_approved" => user.is_approved,
|
"is_approved" => user.is_approved,
|
||||||
|
"is_suggested" => user.is_suggested,
|
||||||
"url" => user.uri || user.ap_id,
|
"url" => user.uri || user.ap_id,
|
||||||
"registration_reason" => user.registration_reason,
|
"registration_reason" => user.registration_reason,
|
||||||
"actor_type" => user.actor_type,
|
"actor_type" => user.actor_type,
|
||||||
|
|
|
@ -216,7 +216,71 @@ def approve_operation do
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
%Schema{
|
%Schema{
|
||||||
description: "POST body for deleting multiple users",
|
description: "POST body for approving multiple users",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
nicknames: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{user: %Schema{type: :array, items: user()}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def suggest_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration"],
|
||||||
|
summary: "Suggest multiple users",
|
||||||
|
operationId: "AdminAPI.UserController.suggest",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for adding multiple suggested users",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
nicknames: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{user: %Schema{type: :array, items: user()}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsuggest_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration"],
|
||||||
|
summary: "Unsuggest multiple users",
|
||||||
|
operationId: "AdminAPI.UserController.unsuggest",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for removing multiple suggested users",
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
nicknames: %Schema{
|
nicknames: %Schema{
|
||||||
|
|
|
@ -191,6 +191,7 @@ def delete_account_operation do
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:password, :query, :string, "Password")
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
],
|
],
|
||||||
|
requestBody: request_body("Parameters", delete_account_request(), required: false),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Success", "application/json", %Schema{
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
@ -237,4 +238,22 @@ def remote_subscribe_operation do
|
||||||
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp delete_account_request do
|
||||||
|
%Schema{
|
||||||
|
title: "AccountDeleteRequest",
|
||||||
|
description: "POST body for deleting one's own account",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
password: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "The user's own password for confirmation.",
|
||||||
|
format: :password
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"password" => "prettyp0ony1313"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@search_limit 40
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
||||||
|
@ -77,7 +79,7 @@ defp search_options(params, user) do
|
||||||
[
|
[
|
||||||
resolve: params[:resolve],
|
resolve: params[:resolve],
|
||||||
following: params[:following],
|
following: params[:following],
|
||||||
limit: params[:limit],
|
limit: min(params[:limit], @search_limit),
|
||||||
offset: params[:offset],
|
offset: params[:offset],
|
||||||
type: params[:type],
|
type: params[:type],
|
||||||
author: get_author(params),
|
author: get_author(params),
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
|
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
|
||||||
|
plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
@ -26,7 +31,90 @@ def index_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def index2_operation do
|
||||||
|
%OpenApiSpex.Operation{
|
||||||
|
tags: ["Suggestions"],
|
||||||
|
summary: "Follow suggestions",
|
||||||
|
operationId: "SuggestionController.index2",
|
||||||
|
responses: %{
|
||||||
|
200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def dismiss_operation do
|
||||||
|
%OpenApiSpex.Operation{
|
||||||
|
tags: ["Suggestions"],
|
||||||
|
summary: "Remove a suggestion",
|
||||||
|
operationId: "SuggestionController.dismiss",
|
||||||
|
parameters: [
|
||||||
|
OpenApiSpex.Operation.parameter(
|
||||||
|
:account_id,
|
||||||
|
:path,
|
||||||
|
%OpenApiSpex.Schema{type: :string},
|
||||||
|
"Account to dismiss",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/suggestions"
|
@doc "GET /api/v1/suggestions"
|
||||||
def index(conn, params),
|
def index(conn, params),
|
||||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||||
|
|
||||||
|
@doc "GET /api/v2/suggestions"
|
||||||
|
def index2(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
limit = Map.get(params, :limit, 40) |> min(80)
|
||||||
|
|
||||||
|
users =
|
||||||
|
%{is_suggested: true, invisible: false, limit: limit}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> exclude_user(user)
|
||||||
|
|> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
|
||||||
|
|> exclude_following(user)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
render(conn, "index.json", %{
|
||||||
|
users: users,
|
||||||
|
source: :staff,
|
||||||
|
for: user,
|
||||||
|
skip_visibility_check: true
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_user(query, %User{id: user_id}) do
|
||||||
|
where(query, [u], u.id != ^user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
|
||||||
|
query
|
||||||
|
|> join(:left, [u], r in UserRelationship,
|
||||||
|
as: :user_relationships,
|
||||||
|
on:
|
||||||
|
r.target_id == u.id and r.source_id == ^user_id and
|
||||||
|
r.relationship_type in ^relationship_types
|
||||||
|
)
|
||||||
|
|> where([user_relationships: r], is_nil(r.target_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp exclude_following(query, %User{id: user_id}) do
|
||||||
|
query
|
||||||
|
|> join(:left, [u], r in FollowingRelationship,
|
||||||
|
as: :following_relationships,
|
||||||
|
on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
|
||||||
|
)
|
||||||
|
|> where([following_relationships: r], is_nil(r.following_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "DELETE /api/v1/suggestions/:account_id"
|
||||||
|
def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
|
||||||
|
with %User{} = target <- User.get_cached_by_id(user_id),
|
||||||
|
{:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
|
||||||
|
json(conn, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -269,6 +269,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
also_known_as: user.also_known_as,
|
also_known_as: user.also_known_as,
|
||||||
is_confirmed: user.is_confirmed,
|
is_confirmed: user.is_confirmed,
|
||||||
|
is_suggested: user.is_suggested,
|
||||||
tags: user.tags,
|
tags: user.tags,
|
||||||
hide_followers_count: user.hide_followers_count,
|
hide_followers_count: user.hide_followers_count,
|
||||||
hide_follows_count: user.hide_follows_count,
|
hide_follows_count: user.hide_follows_count,
|
||||||
|
|
|
@ -59,6 +59,7 @@ def features do
|
||||||
"mastodon_api",
|
"mastodon_api",
|
||||||
"mastodon_api_streaming",
|
"mastodon_api_streaming",
|
||||||
"polls",
|
"polls",
|
||||||
|
"v2_suggestions",
|
||||||
"pleroma_explicit_addressing",
|
"pleroma_explicit_addressing",
|
||||||
"shareable_emoji_packs",
|
"shareable_emoji_packs",
|
||||||
"multifetch",
|
"multifetch",
|
||||||
|
|
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
28
lib/pleroma/web/mastodon_api/views/suggestion_view.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SuggestionView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
|
||||||
|
@source_types [:staff, :global, :past_interactions]
|
||||||
|
|
||||||
|
def render("index.json", %{users: users} = opts) do
|
||||||
|
Enum.map(users, fn user ->
|
||||||
|
opts =
|
||||||
|
opts
|
||||||
|
|> Map.put(:user, user)
|
||||||
|
|> Map.delete(:users)
|
||||||
|
|
||||||
|
render("show.json", opts)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
|
||||||
|
%{
|
||||||
|
source: source,
|
||||||
|
account: AccountView.render("show.json", opts)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,7 +35,9 @@ def get_nodeinfo("2.0") do
|
||||||
openRegistrations: Config.get([:instance, :registrations_open]),
|
openRegistrations: Config.get([:instance, :registrations_open]),
|
||||||
usage: %{
|
usage: %{
|
||||||
users: %{
|
users: %{
|
||||||
total: Map.get(stats, :user_count, 0)
|
total: Map.get(stats, :user_count, 0),
|
||||||
|
activeMonth: Pleroma.User.active_user_count(30),
|
||||||
|
activeHalfyear: Pleroma.User.active_user_count(180)
|
||||||
},
|
},
|
||||||
localPosts: Map.get(stats, :status_count, 0)
|
localPosts: Map.get(stats, :status_count, 0)
|
||||||
},
|
},
|
||||||
|
|
|
@ -192,6 +192,9 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/users/deactivate", UserController, :deactivate)
|
patch("/users/deactivate", UserController, :deactivate)
|
||||||
patch("/users/approve", UserController, :approve)
|
patch("/users/approve", UserController, :approve)
|
||||||
|
|
||||||
|
patch("/users/suggest", UserController, :suggest)
|
||||||
|
patch("/users/unsuggest", UserController, :unsuggest)
|
||||||
|
|
||||||
get("/relay", RelayController, :index)
|
get("/relay", RelayController, :index)
|
||||||
post("/relay", RelayController, :follow)
|
post("/relay", RelayController, :follow)
|
||||||
delete("/relay", RelayController, :unfollow)
|
delete("/relay", RelayController, :unfollow)
|
||||||
|
@ -535,6 +538,7 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/push/subscription", SubscriptionController, :delete)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
|
|
||||||
get("/suggestions", SuggestionController, :index)
|
get("/suggestions", SuggestionController, :index)
|
||||||
|
delete("/suggestions/:account_id", SuggestionController, :dismiss)
|
||||||
|
|
||||||
get("/timelines/home", TimelineController, :home)
|
get("/timelines/home", TimelineController, :home)
|
||||||
get("/timelines/direct", TimelineController, :direct)
|
get("/timelines/direct", TimelineController, :direct)
|
||||||
|
@ -586,6 +590,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/search", SearchController, :search2)
|
get("/search", SearchController, :search2)
|
||||||
|
|
||||||
post("/media", MediaController, :create2)
|
post("/media", MediaController, :create2)
|
||||||
|
|
||||||
|
get("/suggestions", SuggestionController, :index2)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
|
@ -742,6 +748,12 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/manifest.json", ManifestController, :show)
|
get("/manifest.json", ManifestController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:pleroma_html)
|
||||||
|
|
||||||
|
post("/auth/password", TwitterAPI.PasswordController, :request)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/proxy/", Pleroma.Web do
|
scope "/proxy/", Pleroma.Web do
|
||||||
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
|
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
|
||||||
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
|
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
|
||||||
|
|
|
@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||||
|
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
|
plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
|
||||||
|
|
||||||
|
@doc "POST /auth/password"
|
||||||
|
def request(conn, params) do
|
||||||
|
nickname_or_email = params["email"] || params["nickname"]
|
||||||
|
|
||||||
|
TwitterAPI.password_reset(nickname_or_email)
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
|
||||||
def reset(conn, %{"token" => token}) do
|
def reset(conn, %{"token" => token}) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
|
|
|
@ -123,8 +123,10 @@ def change_email(%{assigns: %{user: user}, body_params: body_params} = conn, %{}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do
|
||||||
password = params[:password] || ""
|
# This endpoint can accept a query param or JSON body for backwards-compatibility.
|
||||||
|
# Submitting a JSON body is recommended, so passwords don't end up in server logs.
|
||||||
|
password = body_params[:password] || params[:password] || ""
|
||||||
|
|
||||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
|
|
4
mix.exs
4
mix.exs
|
@ -86,7 +86,7 @@ def application do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Specifies which paths to compile per environment.
|
# Specifies which paths to compile per environment.
|
||||||
defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
|
defp elixirc_paths(:benchmark), do: ["lib", "benchmarks", "priv/scrubbers"]
|
||||||
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
defp elixirc_paths(_), do: ["lib"]
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ defp deps do
|
||||||
{:mock, "~> 0.3.5", only: :test},
|
{:mock, "~> 0.3.5", only: :test},
|
||||||
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
|
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
|
||||||
{:excoveralls, "0.12.3", only: :test},
|
{:excoveralls, "0.12.3", only: :test},
|
||||||
{:hackney, "~> 1.17.0", override: true},
|
{:hackney, "~> 1.18.0", override: true},
|
||||||
{:mox, "~> 1.0", only: :test},
|
{:mox, "~> 1.0", only: :test},
|
||||||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
||||||
] ++ oauth_deps()
|
] ++ oauth_deps()
|
||||||
|
|
4
mix.lock
4
mix.lock
|
@ -11,7 +11,7 @@
|
||||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
||||||
"castore": {:hex, :castore, "0.1.10", "b01a007416a0ae4188e70b3b306236021b16c11474038ead7aff79dd75538c23", [:mix], [], "hexpm", "a48314e0cb45682db2ea27b8ebfa11bd6fa0a6e21a65e5772ad83ca136ff2665"},
|
"castore": {:hex, :castore, "0.1.10", "b01a007416a0ae4188e70b3b306236021b16c11474038ead7aff79dd75538c23", [:mix], [], "hexpm", "a48314e0cb45682db2ea27b8ebfa11bd6fa0a6e21a65e5772ad83ca136ff2665"},
|
||||||
"certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"},
|
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
||||||
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||||
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
|
||||||
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
|
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
|
||||||
"hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
|
"hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
|
||||||
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
|
||||||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"},
|
"http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"},
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.ForcePinnedObjectsToExist do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
execute("UPDATE users SET pinned_objects = '{}' WHERE pinned_objects IS NULL")
|
||||||
|
|
||||||
|
alter table("users") do
|
||||||
|
modify(:pinned_objects, :map, null: false, default: %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
priv/repo/migrations/20211126191138_add_suggestions.exs
Normal file
11
priv/repo/migrations/20211126191138_add_suggestions.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddSuggestions do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:is_suggested, :boolean, default: false, null: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:users, [:is_suggested]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -34,4 +34,14 @@ test "it returns internal users when enabled" do
|
||||||
assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
|
assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "is_suggested param" do
|
||||||
|
_user1 = insert(:user, is_suggested: false)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
assert [^user2] =
|
||||||
|
%{is_suggested: true}
|
||||||
|
|> User.Query.build()
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1718,6 +1718,38 @@ test "delete/1 purges a remote user" do
|
||||||
assert user.banner == %{}
|
assert user.banner == %{}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "set_suggestion" do
|
||||||
|
test "suggests a user" do
|
||||||
|
user = insert(:user, is_suggested: false)
|
||||||
|
refute user.is_suggested
|
||||||
|
{:ok, user} = User.set_suggestion(user, true)
|
||||||
|
assert user.is_suggested
|
||||||
|
end
|
||||||
|
|
||||||
|
test "suggests a list of users" do
|
||||||
|
unsuggested_users = [
|
||||||
|
insert(:user, is_suggested: false),
|
||||||
|
insert(:user, is_suggested: false),
|
||||||
|
insert(:user, is_suggested: false)
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, users} = User.set_suggestion(unsuggested_users, true)
|
||||||
|
|
||||||
|
assert Enum.count(users) == 3
|
||||||
|
|
||||||
|
Enum.each(users, fn user ->
|
||||||
|
assert user.is_suggested
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unsuggests a user" do
|
||||||
|
user = insert(:user, is_suggested: true)
|
||||||
|
assert user.is_suggested
|
||||||
|
{:ok, user} = User.set_suggestion(user, false)
|
||||||
|
refute user.is_suggested
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
||||||
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
||||||
end
|
end
|
||||||
|
@ -2410,13 +2442,16 @@ test "update_last_active_at/1" do
|
||||||
test "active_user_count/1" do
|
test "active_user_count/1" do
|
||||||
insert(:user)
|
insert(:user)
|
||||||
insert(:user, %{local: false})
|
insert(:user, %{local: false})
|
||||||
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -5)})
|
|
||||||
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -3)})
|
|
||||||
insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
|
insert(:user, %{last_active_at: NaiveDateTime.utc_now()})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), days: -15)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -6)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), months: -7)})
|
||||||
|
insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), years: -2)})
|
||||||
|
|
||||||
assert User.active_user_count() == 2
|
assert User.active_user_count() == 2
|
||||||
assert User.active_user_count(6) == 3
|
assert User.active_user_count(180) == 3
|
||||||
assert User.active_user_count(1) == 1
|
assert User.active_user_count(365) == 4
|
||||||
|
assert User.active_user_count(1000) == 5
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "pins" do
|
describe "pins" do
|
||||||
|
|
|
@ -776,6 +776,20 @@ test "doesn't return blocked activities" do
|
||||||
assert Enum.member?(activities, activity_one)
|
assert Enum.member?(activities, activity_one)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "doesn't return activities from deactivated users" do
|
||||||
|
_user = insert(:user)
|
||||||
|
deactivated = insert(:user)
|
||||||
|
active = insert(:user)
|
||||||
|
{:ok, activity_one} = CommonAPI.post(deactivated, %{status: "hey!"})
|
||||||
|
{:ok, activity_two} = CommonAPI.post(active, %{status: "yay!"})
|
||||||
|
{:ok, _updated_user} = User.set_activation(deactivated, false)
|
||||||
|
|
||||||
|
activities = ActivityPub.fetch_activities([], %{})
|
||||||
|
|
||||||
|
refute Enum.member?(activities, activity_one)
|
||||||
|
assert Enum.member?(activities, activity_two)
|
||||||
|
end
|
||||||
|
|
||||||
test "always see your own posts even when they address people you block" do
|
test "always see your own posts even when they address people you block" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
blockee = insert(:user)
|
blockee = insert(:user)
|
||||||
|
|
|
@ -105,5 +105,37 @@ test "it handles image dimensions" do
|
||||||
|
|
||||||
assert attachment.mediaType == "image/jpeg"
|
assert attachment.mediaType == "image/jpeg"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it transforms image dimentions to our internal format" do
|
||||||
|
attachment = %{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960,
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = %AttachmentValidator{
|
||||||
|
type: "Document",
|
||||||
|
name: "Hello world",
|
||||||
|
mediaType: "image/jpeg",
|
||||||
|
blurhash: "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of",
|
||||||
|
url: [
|
||||||
|
%AttachmentValidator.UrlObjectValidator{
|
||||||
|
type: "Link",
|
||||||
|
mediaType: "image/jpeg",
|
||||||
|
href: "https://media.example.tld/1.jpg",
|
||||||
|
width: 880,
|
||||||
|
height: 960
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, ^expected} =
|
||||||
|
AttachmentValidator.cast_and_validate(attachment)
|
||||||
|
|> Ecto.Changeset.apply_action(:insert)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,8 @@ test "it remaps video URLs as attachments if necessary" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
|
"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"width" => 480
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,8 @@ test "it remaps video URLs as attachments if necessary" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
|
"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"height" => 1080
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -107,7 +109,8 @@ test "it works for peertube videos with only their mpegURL map" do
|
||||||
"href" =>
|
"href" =>
|
||||||
"https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4",
|
"https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4",
|
||||||
"mediaType" => "video/mp4",
|
"mediaType" => "video/mp4",
|
||||||
"type" => "Link"
|
"type" => "Link",
|
||||||
|
"height" => 1080
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,4 +524,44 @@ test "returns {:ok, %Object{}} for success case" do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "fix_attachments/1" do
|
||||||
|
test "puts dimensions into attachment url field" do
|
||||||
|
object = %{
|
||||||
|
"attachment" => [
|
||||||
|
%{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960,
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
"attachment" => [
|
||||||
|
%{
|
||||||
|
"type" => "Document",
|
||||||
|
"name" => "Hello world",
|
||||||
|
"url" => [
|
||||||
|
%{
|
||||||
|
"type" => "Link",
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"href" => "https://media.example.tld/1.jpg",
|
||||||
|
"width" => 880,
|
||||||
|
"height" => 960
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mediaType" => "image/jpeg",
|
||||||
|
"blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Transmogrifier.fix_attachments(object) == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -873,6 +873,56 @@ test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
|
||||||
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
|
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
|
||||||
|
user1 = insert(:user, is_suggested: false)
|
||||||
|
user2 = insert(:user, is_suggested: false)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/suggest",
|
||||||
|
%{nicknames: [user1.nickname, user2.nickname]}
|
||||||
|
)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true]
|
||||||
|
[user1, user2] = Repo.reload!([user1, user2])
|
||||||
|
|
||||||
|
assert user1.is_suggested
|
||||||
|
assert user2.is_suggested
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do
|
||||||
|
user1 = insert(:user, is_suggested: true)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch(
|
||||||
|
"/api/pleroma/admin/users/unsuggest",
|
||||||
|
%{nicknames: [user1.nickname, user2.nickname]}
|
||||||
|
)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false]
|
||||||
|
[user1, user2] = Repo.reload!([user1, user2])
|
||||||
|
|
||||||
|
refute user1.is_suggested
|
||||||
|
refute user2.is_suggested
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
|
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -906,6 +956,7 @@ defp user_response(user, attrs \\ %{}) do
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
"is_confirmed" => true,
|
"is_confirmed" => true,
|
||||||
"is_approved" => true,
|
"is_approved" => true,
|
||||||
|
"is_suggested" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil,
|
"registration_reason" => nil,
|
||||||
"actor_type" => "Person",
|
"actor_type" => "Person",
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup do: oauth_access(["read"])
|
setup do: oauth_access(["read", "write"])
|
||||||
|
|
||||||
test "returns empty result", %{conn: conn} do
|
test "returns empty result", %{conn: conn} do
|
||||||
res =
|
res =
|
||||||
|
@ -15,4 +18,66 @@ test "returns empty result", %{conn: conn} do
|
||||||
|
|
||||||
assert res == []
|
assert res == []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions", %{conn: conn} do
|
||||||
|
%{id: user_id} = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"source" => "staff", "account" => %{"id" => ^user_id}}] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding dismissed accounts", %{conn: conn} do
|
||||||
|
%{id: user_id} = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/suggestions/#{user_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding blocked accounts", %{conn: conn, user: blocker} do
|
||||||
|
blocked = insert(:user, is_suggested: true)
|
||||||
|
{:ok, _} = CommonAPI.block(blocker, blocked)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns v2 suggestions excluding followed accounts", %{conn: conn, user: follower} do
|
||||||
|
followed = insert(:user, is_suggested: true)
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(follower, followed)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> get("/api/v2/suggestions")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "dismiss suggestion", %{conn: conn, user: source} do
|
||||||
|
target = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/suggestions/#{target.id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert res == %{}
|
||||||
|
assert UserRelationship.exists?(:suggestion_dismiss, source, target)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,6 +83,7 @@ test "Represent a user account" do
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
is_suggested: false,
|
||||||
hide_favorites: true,
|
hide_favorites: true,
|
||||||
hide_followers: false,
|
hide_followers: false,
|
||||||
hide_follows: false,
|
hide_follows: false,
|
||||||
|
@ -183,6 +184,7 @@ test "Represent a Service(bot) account" do
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
is_moderator: false,
|
is_moderator: false,
|
||||||
|
is_suggested: false,
|
||||||
hide_favorites: true,
|
hide_favorites: true,
|
||||||
hide_followers: false,
|
hide_followers: false,
|
||||||
hide_follows: false,
|
hide_follows: false,
|
||||||
|
|
34
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
Normal file
34
test/pleroma/web/mastodon_api/views/suggestion_view_test.exs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SuggestionViewTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Web.MastodonAPI.SuggestionView, as: View
|
||||||
|
|
||||||
|
test "show.json" do
|
||||||
|
user = insert(:user, is_suggested: true)
|
||||||
|
json = View.render("show.json", %{user: user, source: :staff, skip_visibility_check: true})
|
||||||
|
|
||||||
|
assert json.source == :staff
|
||||||
|
assert json.account.id == user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "index.json" do
|
||||||
|
user1 = insert(:user, is_suggested: true)
|
||||||
|
user2 = insert(:user, is_suggested: true)
|
||||||
|
user3 = insert(:user, is_suggested: true)
|
||||||
|
|
||||||
|
[suggestion1, suggestion2, suggestion3] =
|
||||||
|
View.render("index.json", %{
|
||||||
|
users: [user1, user2, user3],
|
||||||
|
source: :staff,
|
||||||
|
skip_visibility_check: true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert suggestion1.source == :staff
|
||||||
|
assert suggestion2.account.id == user2.id
|
||||||
|
assert suggestion3.account.url == user3.ap_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -95,6 +95,7 @@ test "api routes are detected correctly" do
|
||||||
".well-known",
|
".well-known",
|
||||||
"nodeinfo",
|
"nodeinfo",
|
||||||
"manifest.json",
|
"manifest.json",
|
||||||
|
"auth",
|
||||||
"proxy",
|
"proxy",
|
||||||
"test",
|
"test",
|
||||||
"user_exists",
|
"user_exists",
|
||||||
|
|
|
@ -5,10 +5,14 @@
|
||||||
defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
|
defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.PasswordResetToken
|
alias Pleroma.PasswordResetToken
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
import Swoosh.TestAssertions
|
||||||
|
|
||||||
describe "GET /api/pleroma/password_reset/token" do
|
describe "GET /api/pleroma/password_reset/token" do
|
||||||
test "it returns error when token invalid", %{conn: conn} do
|
test "it returns error when token invalid", %{conn: conn} do
|
||||||
|
@ -116,4 +120,94 @@ test "it sets password_reset_pending to false", %{conn: conn} do
|
||||||
assert User.get_by_id(user.id).password_reset_pending == false
|
assert User.get_by_id(user.id).password_reset_pending == false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /auth/password, with valid parameters" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
conn = post(conn, "/auth/password?email=#{user.email}")
|
||||||
|
%{conn: conn, user: user}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 204", %{conn: conn} do
|
||||||
|
assert empty_json_response(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates a PasswordResetToken record for user", %{user: user} do
|
||||||
|
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
|
||||||
|
assert token_record
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it sends an email to user", %{user: user} do
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
|
||||||
|
|
||||||
|
email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: {user.name, user.email},
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /auth/password, with nickname" do
|
||||||
|
test "it returns 204", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert conn
|
||||||
|
|> post("/auth/password?nickname=#{user.nickname}")
|
||||||
|
|> empty_json_response()
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
|
||||||
|
|
||||||
|
email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
|
||||||
|
notify_email = Config.get([:instance, :notify_email])
|
||||||
|
instance_name = Config.get([:instance, :name])
|
||||||
|
|
||||||
|
assert_email_sent(
|
||||||
|
from: {instance_name, notify_email},
|
||||||
|
to: {user.name, user.email},
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't fail when a user has no email", %{conn: conn} do
|
||||||
|
user = insert(:user, %{email: nil})
|
||||||
|
|
||||||
|
assert conn
|
||||||
|
|> post("/auth/password?nickname=#{user.nickname}")
|
||||||
|
|> empty_json_response()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /auth/password, with invalid parameters" do
|
||||||
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, user: user}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 204 when user is not found", %{conn: conn, user: user} do
|
||||||
|
conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
|
||||||
|
|
||||||
|
assert empty_json_response(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 204 when user is not local", %{conn: conn, user: user} do
|
||||||
|
{:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false))
|
||||||
|
conn = post(conn, "/auth/password?email=#{user.email}")
|
||||||
|
|
||||||
|
assert empty_json_response(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 204 when user is deactivated", %{conn: conn, user: user} do
|
||||||
|
{:ok, user} = Repo.update(Ecto.Changeset.change(user, is_active: false, local: true))
|
||||||
|
conn = post(conn, "/auth/password?email=#{user.email}")
|
||||||
|
|
||||||
|
assert empty_json_response(conn)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -473,7 +473,10 @@ test "without permissions", %{conn: conn} do
|
||||||
|
|
||||||
test "with proper permissions and wrong or missing password", %{conn: conn} do
|
test "with proper permissions and wrong or missing password", %{conn: conn} do
|
||||||
for params <- [%{"password" => "hi"}, %{}] do
|
for params <- [%{"password" => "hi"}, %{}] do
|
||||||
ret_conn = post(conn, "/api/pleroma/delete_account", params)
|
ret_conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/delete_account", params)
|
||||||
|
|
||||||
assert json_response_and_validate_schema(ret_conn, 200) == %{
|
assert json_response_and_validate_schema(ret_conn, 200) == %{
|
||||||
"error" => "Invalid password."
|
"error" => "Invalid password."
|
||||||
|
@ -481,8 +484,28 @@ test "with proper permissions and wrong or missing password", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with proper permissions and valid password", %{conn: conn, user: user} do
|
test "with proper permissions and valid password (URL query)", %{conn: conn, user: user} do
|
||||||
conn = post(conn, "/api/pleroma/delete_account?password=test")
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/delete_account?password=test")
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
refute user.is_active
|
||||||
|
assert user.name == nil
|
||||||
|
assert user.bio == ""
|
||||||
|
assert user.password_hash == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with proper permissions and valid password (JSON body)", %{conn: conn, user: user} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/delete_account", %{password: "test"})
|
||||||
|
|
||||||
ObanHelpers.perform_all()
|
ObanHelpers.perform_all()
|
||||||
assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
|
assert json_response_and_validate_schema(conn, 200) == %{"status" => "success"}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue