Merge remote-tracking branch 'origin/develop' into manifest

This commit is contained in:
Alex Gleason 2021-12-19 11:33:10 -06:00
commit e4f9cb1c1b
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
45 changed files with 838 additions and 66 deletions

View file

@ -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

View file

@ -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 &&\

View file

@ -394,7 +394,7 @@ defmodule Pleroma.LoadTesting.Activities do
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()}"

View file

@ -99,15 +99,16 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags 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

View file

@ -17,14 +17,14 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines 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 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines 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 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines 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 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines 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},

View file

@ -4,8 +4,7 @@ import Config
# 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 @@ config :pleroma, Pleroma.Repo,
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

View file

@ -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

View file

@ -362,11 +362,9 @@ defmodule Pleroma.Activity 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

View file

@ -9,7 +9,8 @@ defenum(Pleroma.UserRelationship.Type,
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,

View file

@ -103,6 +103,7 @@ defmodule Pleroma.Emoji.Loader 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 @@ defmodule Pleroma.Emoji.Loader 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])

View file

@ -338,6 +338,26 @@ defmodule Pleroma.ModerationLog do
"@#{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},

View file

@ -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 @@ defmodule Pleroma.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 @@ defmodule Pleroma.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)

View file

@ -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 @@ defmodule Pleroma.User.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)

View file

@ -68,12 +68,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator 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 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator 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

View file

@ -63,18 +63,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
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, [
[ {"Content-Type", "application/activity+json"},
{"Content-Type", "application/activity+json"}, {"Date", date},
{"Date", date}, {"signature", signature},
{"signature", signature}, {"digest", digest}
{"digest", digest} ]
] ) do
) do
if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
Instances.set_reachable(inbox) Instances.set_reachable(inbox)
end end

View file

@ -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 @@ defmodule Pleroma.Web.AdminAPI.UserController do
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])

View file

@ -80,6 +80,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView 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,

View file

@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation 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{

View file

@ -191,6 +191,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation 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 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation 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

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.SearchController 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),

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController 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

View file

@ -269,6 +269,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView 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,

View file

@ -59,6 +59,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView 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",

View 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

View file

@ -35,7 +35,9 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo 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)
}, },

View file

@ -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)

View file

@ -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}),

View file

@ -123,8 +123,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
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} ->

View file

@ -86,7 +86,7 @@ defmodule Pleroma.Mixfile 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 @@ defmodule Pleroma.Mixfile 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()

View file

@ -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"},

View file

@ -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

View 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

View file

@ -34,4 +34,14 @@ defmodule Pleroma.User.QueryTest 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

View file

@ -1718,6 +1718,38 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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

View file

@ -776,6 +776,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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)

View file

@ -105,5 +105,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest 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

View file

@ -58,7 +58,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest 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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest 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 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest 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
} }
] ]
} }

View file

@ -524,4 +524,44 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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

View file

@ -873,6 +873,56 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest 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",

View file

@ -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 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest 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

View file

@ -83,6 +83,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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,

View 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

View file

@ -95,6 +95,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
".well-known", ".well-known",
"nodeinfo", "nodeinfo",
"manifest.json", "manifest.json",
"auth",
"proxy", "proxy",
"test", "test",
"user_exists", "user_exists",

View file

@ -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 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest 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

View file

@ -473,7 +473,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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"}