@@ -5,11 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
## Unreleased | |||
### Added | |||
- Experimental websocket-based federation between Pleroma instances. | |||
### Changed | |||
- Search: Users are now findable by their urls. | |||
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated. | |||
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated. | |||
- The `discoverable` field in the `User` struct will now add a NOINDEX metatag to profile pages when false. | |||
@@ -19,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
### Added | |||
- Media preview proxy (requires media proxy be enabled; see `:media_preview_proxy` config for more details). | |||
- Pleroma API: Importing the mutes users from CSV files. | |||
- Experimental websocket-based federation between Pleroma instances. | |||
- Admin API: Importing emoji from a zip file | |||
### Removed | |||
@@ -28,6 +28,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- Removed `:managed_config` option. In practice, it was accidentally removed with 2.0.0 release when frontends were | |||
switched to a new configuration mechanism, however it was not officially removed until now. | |||
### Fixed | |||
- Add documented-but-missing chat pagination. | |||
- Allow sending out emails again. | |||
## [2.1.2] - 2020-09-17 | |||
@@ -18,15 +18,16 @@ If you are running Linux (glibc or musl) on x86/arm, the recommended way to inst | |||
### From Source | |||
If your platform is not supported, or you just want to be able to edit the source code easily, you may install Pleroma from source. | |||
- [Debian-based](https://docs-develop.pleroma.social/backend/installation/debian_based_en/) | |||
- [Debian-based (jp)](https://docs-develop.pleroma.social/backend/installation/debian_based_jp/) | |||
- [Alpine Linux](https://docs-develop.pleroma.social/backend/installation/alpine_linux_en/) | |||
- [Arch Linux](https://docs-develop.pleroma.social/backend/installation/arch_linux_en/) | |||
- [CentOS 7](https://docs-develop.pleroma.social/backend/installation/centos7_en/) | |||
- [Debian-based](https://docs-develop.pleroma.social/backend/installation/debian_based_en/) | |||
- [Debian-based (jp)](https://docs-develop.pleroma.social/backend/installation/debian_based_jp/) | |||
- [FreeBSD](https://docs-develop.pleroma.social/backend/installation/freebsd_en/) | |||
- [Gentoo Linux](https://docs-develop.pleroma.social/backend/installation/gentoo_en/) | |||
- [NetBSD](https://docs-develop.pleroma.social/backend/installation/netbsd_en/) | |||
- [OpenBSD](https://docs-develop.pleroma.social/backend/installation/openbsd_en/) | |||
- [OpenBSD (fi)](https://docs-develop.pleroma.social/backend/installation/openbsd_fi/) | |||
- [CentOS 7](https://docs-develop.pleroma.social/backend/installation/centos7_en/) | |||
### OS/Distro packages | |||
Currently Pleroma is not packaged by any OS/Distros, but if you want to package it for one, we can guide you through the process on our [community channels](#community-channels). If you want to change default options in your Pleroma package, please **discuss it with us first**. | |||
@@ -18,6 +18,7 @@ defmodule Pleroma.Chat do | |||
It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages. | |||
""" | |||
@type t :: %__MODULE__{} | |||
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} | |||
schema "chats" do | |||
@@ -41,16 +42,28 @@ defmodule Pleroma.Chat do | |||
|> unique_constraint(:user_id, name: :chats_user_id_recipient_index) | |||
end | |||
@spec get_by_user_and_id(User.t(), FlakeId.Ecto.CompatType.t()) :: | |||
{:ok, t()} | {:error, :not_found} | |||
def get_by_user_and_id(%User{id: user_id}, id) do | |||
from(c in __MODULE__, | |||
where: c.id == ^id, | |||
where: c.user_id == ^user_id | |||
) | |||
|> Repo.find_resource() | |||
end | |||
@spec get_by_id(FlakeId.Ecto.CompatType.t()) :: t() | nil | |||
def get_by_id(id) do | |||
__MODULE__ | |||
|> Repo.get(id) | |||
Repo.get(__MODULE__, id) | |||
end | |||
@spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil | |||
def get(user_id, recipient) do | |||
__MODULE__ | |||
|> Repo.get_by(user_id: user_id, recipient: recipient) | |||
Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient) | |||
end | |||
@spec get_or_create(FlakeId.Ecto.CompatType.t(), String.t()) :: | |||
{:ok, t()} | {:error, Ecto.Changeset.t()} | |||
def get_or_create(user_id, recipient) do | |||
%__MODULE__{} | |||
|> changeset(%{user_id: user_id, recipient: recipient}) | |||
@@ -62,6 +75,8 @@ defmodule Pleroma.Chat do | |||
) | |||
end | |||
@spec bump_or_create(FlakeId.Ecto.CompatType.t(), String.t()) :: | |||
{:ok, t()} | {:error, Ecto.Changeset.t()} | |||
def bump_or_create(user_id, recipient) do | |||
%__MODULE__{} | |||
|> changeset(%{user_id: user_id, recipient: recipient}) | |||
@@ -35,6 +35,11 @@ defmodule Pleroma.Emails.Mailer do | |||
def deliver(email, config \\ []) | |||
def deliver(email, config) do | |||
# temporary hackney fix until hackney max_connections bug is fixed | |||
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101 | |||
email = | |||
Swoosh.Email.put_private(email, :hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) | |||
case enabled?() do | |||
true -> Swoosh.Mailer.deliver(email, parse_config(config)) | |||
false -> {:error, :deliveries_disabled} | |||
@@ -56,6 +56,9 @@ defmodule Pleroma.Emoji do | |||
end | |||
end | |||
@spec exist?(String.t()) :: boolean() | |||
def exist?(name), do: not is_nil(get(name)) | |||
@doc "Returns all the emojos!!" | |||
@spec get_all() :: list({String.t(), String.t(), String.t()}) | |||
def get_all do | |||
@@ -17,6 +17,7 @@ defmodule Pleroma.Emoji.Pack do | |||
} | |||
alias Pleroma.Emoji | |||
alias Pleroma.Emoji.Pack | |||
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} | |||
def create(name) do | |||
@@ -64,24 +65,93 @@ defmodule Pleroma.Emoji.Pack do | |||
end | |||
end | |||
@spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) :: | |||
{:ok, t()} | {:error, File.posix() | atom()} | |||
def add_file(name, shortcode, filename, file) do | |||
with :ok <- validate_not_empty([name, shortcode, filename]), | |||
@spec unpack_zip_emojies(list(tuple())) :: list(map()) | |||
defp unpack_zip_emojies(zip_files) do | |||
Enum.reduce(zip_files, [], fn | |||
{_, path, s, _, _, _}, acc when elem(s, 2) == :regular -> | |||
with( | |||
filename <- Path.basename(path), | |||
shortcode <- Path.basename(filename, Path.extname(filename)), | |||
false <- Emoji.exist?(shortcode) | |||
) do | |||
[%{path: path, filename: path, shortcode: shortcode} | acc] | |||
else | |||
_ -> acc | |||
end | |||
_, acc -> | |||
acc | |||
end) | |||
end | |||
@spec add_file(t(), String.t(), Path.t(), Plug.Upload.t()) :: | |||
{:ok, t()} | |||
| {:error, File.posix() | atom()} | |||
def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do | |||
with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), | |||
[_ | _] = emojies <- unpack_zip_emojies(zip_files), | |||
{:ok, tmp_dir} <- Pleroma.Utils.tmp_dir("emoji") do | |||
try do | |||
{:ok, _emoji_files} = | |||
:zip.unzip( | |||
to_charlist(file.path), | |||
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}] | |||
) | |||
{_, updated_pack} = | |||
Enum.map_reduce(emojies, pack, fn item, emoji_pack -> | |||
emoji_file = %Plug.Upload{ | |||
filename: item[:filename], | |||
path: Path.join(tmp_dir, item[:path]) | |||
} | |||
{:ok, updated_pack} = | |||
do_add_file( | |||
emoji_pack, | |||
item[:shortcode], | |||
to_string(item[:filename]), | |||
emoji_file | |||
) | |||
{item, updated_pack} | |||
end) | |||
Emoji.reload() | |||
{:ok, updated_pack} | |||
after | |||
File.rm_rf(tmp_dir) | |||
end | |||
else | |||
{:error, _} = error -> | |||
error | |||
_ -> | |||
{:ok, pack} | |||
end | |||
end | |||
def add_file(%Pack{} = pack, shortcode, filename, %Plug.Upload{} = file) do | |||
with :ok <- validate_not_empty([shortcode, filename]), | |||
:ok <- validate_emoji_not_exists(shortcode), | |||
{:ok, pack} <- load_pack(name), | |||
:ok <- save_file(file, pack, filename), | |||
{:ok, updated_pack} <- pack |> put_emoji(shortcode, filename) |> save_pack() do | |||
{:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do | |||
Emoji.reload() | |||
{:ok, updated_pack} | |||
end | |||
end | |||
@spec delete_file(String.t(), String.t()) :: | |||
defp do_add_file(pack, shortcode, filename, file) do | |||
with :ok <- save_file(file, pack, filename) do | |||
pack | |||
|> put_emoji(shortcode, filename) | |||
|> save_pack() | |||
end | |||
end | |||
@spec delete_file(t(), String.t()) :: | |||
{:ok, t()} | {:error, File.posix() | atom()} | |||
def delete_file(name, shortcode) do | |||
with :ok <- validate_not_empty([name, shortcode]), | |||
{:ok, pack} <- load_pack(name), | |||
def delete_file(%Pack{} = pack, shortcode) do | |||
with :ok <- validate_not_empty([shortcode]), | |||
:ok <- remove_file(pack, shortcode), | |||
{:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do | |||
Emoji.reload() | |||
@@ -89,11 +159,10 @@ defmodule Pleroma.Emoji.Pack do | |||
end | |||
end | |||
@spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) :: | |||
@spec update_file(t(), String.t(), String.t(), String.t(), boolean()) :: | |||
{:ok, t()} | {:error, File.posix() | atom()} | |||
def update_file(name, shortcode, new_shortcode, new_filename, force) do | |||
with :ok <- validate_not_empty([name, shortcode, new_shortcode, new_filename]), | |||
{:ok, pack} <- load_pack(name), | |||
def update_file(%Pack{} = pack, shortcode, new_shortcode, new_filename, force) do | |||
with :ok <- validate_not_empty([shortcode, new_shortcode, new_filename]), | |||
{:ok, filename} <- get_filename(pack, shortcode), | |||
:ok <- validate_emoji_not_exists(new_shortcode, force), | |||
:ok <- rename_file(pack, filename, new_filename), | |||
@@ -243,9 +312,10 @@ defmodule Pleroma.Emoji.Pack do | |||
defp validate_emoji_not_exists(_shortcode, true), do: :ok | |||
defp validate_emoji_not_exists(shortcode, _) do | |||
case Emoji.get(shortcode) do | |||
nil -> :ok | |||
_ -> {:error, :already_exists} | |||
if Emoji.exist?(shortcode) do | |||
{:error, :already_exists} | |||
else | |||
:ok | |||
end | |||
end | |||
@@ -386,25 +456,18 @@ defmodule Pleroma.Emoji.Pack do | |||
end | |||
end | |||
defp save_file(file, pack, filename) do | |||
defp save_file(%Plug.Upload{path: upload_path}, pack, filename) do | |||
file_path = Path.join(pack.path, filename) | |||
create_subdirs(file_path) | |||
case file do | |||
%Plug.Upload{path: upload_path} -> | |||
# Copy the uploaded file from the temporary directory | |||
with {:ok, _} <- File.copy(upload_path, file_path), do: :ok | |||
url when is_binary(url) -> | |||
# Download and write the file | |||
file_contents = Tesla.get!(url).body | |||
File.write(file_path, file_contents) | |||
with {:ok, _} <- File.copy(upload_path, file_path) do | |||
:ok | |||
end | |||
end | |||
defp put_emoji(pack, shortcode, filename) do | |||
files = Map.put(pack.files, shortcode, filename) | |||
%{pack | files: files} | |||
%{pack | files: files, files_count: length(Map.keys(files))} | |||
end | |||
defp delete_emoji(pack, shortcode) do | |||
@@ -47,6 +47,7 @@ defmodule Pleroma.User.Query do | |||
is_moderator: boolean(), | |||
super_users: boolean(), | |||
invisible: boolean(), | |||
internal: boolean(), | |||
followers: User.t(), | |||
friends: User.t(), | |||
recipients_from_activity: [String.t()], | |||
@@ -80,7 +81,9 @@ defmodule Pleroma.User.Query do | |||
end | |||
defp prepare_query(query, criteria) do | |||
Enum.reduce(criteria, query, &compose_query/2) | |||
criteria | |||
|> Map.put_new(:internal, false) | |||
|> Enum.reduce(query, &compose_query/2) | |||
end | |||
defp compose_query({key, value}, query) | |||
@@ -129,14 +132,12 @@ defmodule Pleroma.User.Query do | |||
defp compose_query({:active, _}, query) do | |||
User.restrict_deactivated(query) | |||
|> where([u], not is_nil(u.nickname)) | |||
|> where([u], u.approval_pending == false) | |||
end | |||
defp compose_query({:legacy_active, _}, query) do | |||
query | |||
|> where([u], fragment("not (?->'deactivated' @> 'true')", u.info)) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
defp compose_query({:deactivated, false}, query) do | |||
@@ -145,7 +146,6 @@ defmodule Pleroma.User.Query do | |||
defp compose_query({:deactivated, true}, query) do | |||
where(query, [u], u.deactivated == ^true) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
defp compose_query({:need_approval, _}, query) do | |||
@@ -199,10 +199,15 @@ defmodule Pleroma.User.Query do | |||
limit(query, ^limit) | |||
end | |||
defp compose_query({:internal, false}, query) do | |||
query | |||
|> where([u], not is_nil(u.nickname)) | |||
|> where([u], not like(u.nickname, "internal.%")) | |||
end | |||
defp compose_query(_unsupported_param, query), do: query | |||
defp location_query(query, local) do | |||
where(query, [u], u.local == ^local) | |||
|> where([u], not is_nil(u.nickname)) | |||
end | |||
end |
@@ -3,8 +3,10 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.User.Search do | |||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType | |||
alias Pleroma.Pagination | |||
alias Pleroma.User | |||
import Ecto.Query | |||
@limit 20 | |||
@@ -19,16 +21,46 @@ defmodule Pleroma.User.Search do | |||
query_string = format_query(query_string) | |||
maybe_resolve(resolve, for_user, query_string) | |||
# If this returns anything, it should bounce to the top | |||
maybe_resolved = maybe_resolve(resolve, for_user, query_string) | |||
top_user_ids = | |||
[] | |||
|> maybe_add_resolved(maybe_resolved) | |||
|> maybe_add_ap_id_match(query_string) | |||
|> maybe_add_uri_match(query_string) | |||
results = | |||
query_string | |||
|> search_query(for_user, following) | |||
|> search_query(for_user, following, top_user_ids) | |||
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) | |||
results | |||
end | |||
defp maybe_add_resolved(list, {:ok, %User{} = user}) do | |||
[user.id | list] | |||
end | |||
defp maybe_add_resolved(list, _), do: list | |||
defp maybe_add_ap_id_match(list, query) do | |||
if user = User.get_cached_by_ap_id(query) do | |||
[user.id | list] | |||
else | |||
list | |||
end | |||
end | |||
defp maybe_add_uri_match(list, query) do | |||
with {:ok, query} <- UriType.cast(query), | |||
%User{} = user <- Pleroma.Repo.get_by(User, uri: query) do | |||
[user.id | list] | |||
else | |||
_ -> list | |||
end | |||
end | |||
defp format_query(query_string) do | |||
# Strip the beginning @ off if there is a query | |||
query_string = String.trim_leading(query_string, "@") | |||
@@ -47,7 +79,7 @@ defmodule Pleroma.User.Search do | |||
end | |||
end | |||
defp search_query(query_string, for_user, following) do | |||
defp search_query(query_string, for_user, following, top_user_ids) do | |||
for_user | |||
|> base_query(following) | |||
|> filter_blocked_user(for_user) | |||
@@ -56,13 +88,20 @@ defmodule Pleroma.User.Search do | |||
|> filter_internal_users() | |||
|> filter_blocked_domains(for_user) | |||
|> fts_search(query_string) | |||
|> select_top_users(top_user_ids) | |||
|> trigram_rank(query_string) | |||
|> boost_search_rank(for_user) | |||
|> boost_search_rank(for_user, top_user_ids) | |||
|> subquery() | |||
|> order_by(desc: :search_rank) | |||
|> maybe_restrict_local(for_user) | |||
end | |||
defp select_top_users(query, top_user_ids) do | |||
from(u in query, | |||
or_where: u.id in ^top_user_ids | |||
) | |||
end | |||
defp fts_search(query, query_string) do | |||
query_string = to_tsquery(query_string) | |||
@@ -180,7 +219,7 @@ defmodule Pleroma.User.Search do | |||
defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) | |||
defp boost_search_rank(query, %User{} = for_user) do | |||
defp boost_search_rank(query, %User{} = for_user, top_user_ids) do | |||
friends_ids = User.get_friends_ids(for_user) | |||
followers_ids = User.get_followers_ids(for_user) | |||
@@ -192,6 +231,7 @@ defmodule Pleroma.User.Search do | |||
CASE WHEN (?) THEN (?) * 1.5 | |||
WHEN (?) THEN (?) * 1.3 | |||
WHEN (?) THEN (?) * 1.1 | |||
WHEN (?) THEN 9001 | |||
ELSE (?) END | |||
""", | |||
u.id in ^friends_ids and u.id in ^followers_ids, | |||
@@ -200,11 +240,26 @@ defmodule Pleroma.User.Search do | |||
u.search_rank, | |||
u.id in ^followers_ids, | |||
u.search_rank, | |||
u.id in ^top_user_ids, | |||
u.search_rank | |||
) | |||
} | |||
) | |||
end | |||
defp boost_search_rank(query, _for_user), do: query | |||
defp boost_search_rank(query, _for_user, top_user_ids) do | |||
from(u in subquery(query), | |||
select_merge: %{ | |||
search_rank: | |||
fragment( | |||
""" | |||
CASE WHEN (?) THEN 9001 | |||
ELSE (?) END | |||
""", | |||
u.id in ^top_user_ids, | |||
u.search_rank | |||
) | |||
} | |||
) | |||
end | |||
end |
@@ -24,4 +24,24 @@ defmodule Pleroma.Utils do | |||
def command_available?(command) do | |||
match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"])) | |||
end | |||
@doc "creates the uniq temporary directory" | |||
@spec tmp_dir(String.t()) :: {:ok, String.t()} | {:error, :file.posix()} | |||
def tmp_dir(prefix \\ "") do | |||
sub_dir = | |||
[ | |||
prefix, | |||
Timex.to_unix(Timex.now()), | |||
:os.getpid(), | |||
String.downcase(Integer.to_string(:rand.uniform(0x100000000), 36)) | |||
] | |||
|> Enum.join("-") | |||
tmp_dir = Path.join(System.tmp_dir!(), sub_dir) | |||
case File.mkdir(tmp_dir) do | |||
:ok -> {:ok, tmp_dir} | |||
error -> error | |||
end | |||
end | |||
end |
@@ -515,15 +515,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
end | |||
def handle_incoming( | |||
%{"type" => "Create", "object" => %{"type" => objtype}} = data, | |||
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data, | |||
_options | |||
) | |||
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do | |||
data = Map.put(data, "object", strip_internal_fields(data["object"])) | |||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), | |||
nil <- Activity.get_create_by_object_ap_id(obj_id), | |||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do | |||
{:ok, activity} | |||
else | |||
%Activity{} = activity -> {:ok, activity} | |||
e -> e | |||
end | |||
end | |||
@@ -158,7 +158,8 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do | |||
"The messages in the chat", | |||
"application/json", | |||
chat_messages_response() | |||
) | |||
), | |||
404 => Operation.response("Not Found", "application/json", ApiError) | |||
}, | |||
security: [ | |||
%{ | |||
@@ -0,0 +1,139 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do | |||
alias OpenApiSpex.Operation | |||
alias OpenApiSpex.Schema | |||
alias Pleroma.Web.ApiSpec.Schemas.ApiError | |||
import Pleroma.Web.ApiSpec.Helpers | |||
def open_api_operation(action) do | |||
operation = String.to_existing_atom("#{action}_operation") | |||
apply(__MODULE__, operation, []) | |||
end | |||
def create_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Add new file to the pack", | |||
operationId: "PleromaAPI.EmojiPackController.add_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
requestBody: request_body("Parameters", create_request(), required: true), | |||
parameters: [name_param()], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
422 => Operation.response("Unprocessable Entity", "application/json", ApiError), | |||
404 => Operation.response("Not Found", "application/json", ApiError), | |||
400 => Operation.response("Bad Request", "application/json", ApiError), | |||
409 => Operation.response("Conflict", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
defp create_request do | |||
%Schema{ | |||
type: :object, | |||
required: [:file], | |||
properties: %{ | |||
file: %Schema{ | |||
description: | |||
"File needs to be uploaded with the multipart request or link to remote file", | |||
anyOf: [ | |||
%Schema{type: :string, format: :binary}, | |||
%Schema{type: :string, format: :uri} | |||
] | |||
}, | |||
shortcode: %Schema{ | |||
type: :string, | |||
description: | |||
"Shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename." | |||
}, | |||
filename: %Schema{ | |||
type: :string, | |||
description: | |||
"New emoji file name. If not specified will be taken from original filename." | |||
} | |||
} | |||
} | |||
end | |||
def update_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Add new file to the pack", | |||
operationId: "PleromaAPI.EmojiPackController.update_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
requestBody: request_body("Parameters", update_request(), required: true), | |||
parameters: [name_param()], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
404 => Operation.response("Not Found", "application/json", ApiError), | |||
400 => Operation.response("Bad Request", "application/json", ApiError), | |||
409 => Operation.response("Conflict", "application/json", ApiError), | |||
422 => Operation.response("Unprocessable Entity", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
defp update_request do | |||
%Schema{ | |||
type: :object, | |||
required: [:shortcode, :new_shortcode, :new_filename], | |||
properties: %{ | |||
shortcode: %Schema{ | |||
type: :string, | |||
description: "Emoji file shortcode" | |||
}, | |||
new_shortcode: %Schema{ | |||
type: :string, | |||
description: "New emoji file shortcode" | |||
}, | |||
new_filename: %Schema{ | |||
type: :string, | |||
description: "New filename for emoji file" | |||
}, | |||
force: %Schema{ | |||
type: :boolean, | |||
description: "With true value to overwrite existing emoji with new shortcode", | |||
default: false | |||
} | |||
} | |||
} | |||
end | |||
def delete_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Delete emoji file from pack", | |||
operationId: "PleromaAPI.EmojiPackController.delete_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
parameters: [ | |||
name_param(), | |||
Operation.parameter(:shortcode, :query, :string, "File shortcode", | |||
example: "cofe", | |||
required: true | |||
) | |||
], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
400 => Operation.response("Bad Request", "application/json", ApiError), | |||
404 => Operation.response("Not Found", "application/json", ApiError), | |||
422 => Operation.response("Unprocessable Entity", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
defp name_param do | |||
Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true) | |||
end | |||
defp files_object do | |||
%Schema{ | |||
type: :object, | |||
additionalProperties: %Schema{type: :string}, | |||
description: "Object with emoji names as keys and filenames as values" | |||
} | |||
end | |||
end |
@@ -175,111 +175,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do | |||
} | |||
end | |||
def add_file_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Add new file to the pack", | |||
operationId: "PleromaAPI.EmojiPackController.add_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
requestBody: request_body("Parameters", add_file_request(), required: true), | |||
parameters: [name_param()], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
400 => Operation.response("Bad Request", "application/json", ApiError), | |||
409 => Operation.response("Conflict", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
defp add_file_request do | |||
%Schema{ | |||
type: :object, | |||
required: [:file], | |||
properties: %{ | |||
file: %Schema{ | |||
description: | |||
"File needs to be uploaded with the multipart request or link to remote file", | |||
anyOf: [ | |||
%Schema{type: :string, format: :binary}, | |||
%Schema{type: :string, format: :uri} | |||
] | |||
}, | |||
shortcode: %Schema{ | |||
type: :string, | |||
description: | |||
"Shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename." | |||
}, | |||
filename: %Schema{ | |||
type: :string, | |||
description: | |||
"New emoji file name. If not specified will be taken from original filename." | |||
} | |||
} | |||
} | |||
end | |||
def update_file_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Add new file to the pack", | |||
operationId: "PleromaAPI.EmojiPackController.update_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
requestBody: request_body("Parameters", update_file_request(), required: true), | |||
parameters: [name_param()], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
400 => Operation.response("Bad Request", "application/json", ApiError), | |||
409 => Operation.response("Conflict", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
defp update_file_request do | |||
%Schema{ | |||
type: :object, | |||
required: [:shortcode, :new_shortcode, :new_filename], | |||
properties: %{ | |||
shortcode: %Schema{ | |||
type: :string, | |||
description: "Emoji file shortcode" | |||
}, | |||
new_shortcode: %Schema{ | |||
type: :string, | |||
description: "New emoji file shortcode" | |||
}, | |||
new_filename: %Schema{ | |||
type: :string, | |||
description: "New filename for emoji file" | |||
}, | |||
force: %Schema{ | |||
type: :boolean, | |||
description: "With true value to overwrite existing emoji with new shortcode", | |||
default: false | |||
} | |||
} | |||
} | |||
end | |||
def delete_file_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
summary: "Delete emoji file from pack", | |||
operationId: "PleromaAPI.EmojiPackController.delete_file", | |||
security: [%{"oAuth" => ["write"]}], | |||
parameters: [ | |||
name_param(), | |||
Operation.parameter(:shortcode, :query, :string, "File shortcode", | |||
example: "cofe", | |||
required: true | |||
) | |||
], | |||
responses: %{ | |||
200 => Operation.response("Files Object", "application/json", files_object()), | |||
400 => Operation.response("Bad Request", "application/json", ApiError) | |||
} | |||
} | |||
end | |||
def import_from_filesystem_operation do | |||
%Operation{ | |||
tags: ["Emoji Packs"], | |||
@@ -48,13 +48,13 @@ defmodule Pleroma.Web.ControllerHelper do | |||
defp param_to_integer(_, default), do: default | |||
def add_link_headers(conn, activities, extra_params \\ %{}) | |||
def add_link_headers(conn, entries, extra_params \\ %{}) | |||
def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities, _extra_params), | |||
def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _entries, _extra_params), | |||
do: conn | |||
def add_link_headers(conn, activities, extra_params) do | |||
case get_pagination_fields(conn, activities, extra_params) do | |||
def add_link_headers(conn, entries, extra_params) do | |||
case get_pagination_fields(conn, entries, extra_params) do | |||
%{"next" => next_url, "prev" => prev_url} -> | |||
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") | |||
@@ -78,19 +78,15 @@ defmodule Pleroma.Web.ControllerHelper do | |||
} | |||
end | |||
def get_pagination_fields(conn, activities, extra_params \\ %{}) do | |||
case List.last(activities) do | |||
def get_pagination_fields(conn, entries, extra_params \\ %{}) do | |||
case List.last(entries) do | |||
%{pagination_id: max_id} when not is_nil(max_id) -> | |||
%{pagination_id: min_id} = | |||
activities | |||
|> List.first() | |||
%{pagination_id: min_id} = List.first(entries) | |||
build_pagination_fields(conn, min_id, max_id, extra_params) | |||
%{id: max_id} -> | |||
%{id: min_id} = | |||
activities | |||
|> List.first() | |||
%{id: min_id} = List.first(entries) | |||
build_pagination_fields(conn, min_id, max_id, extra_params) | |||
@@ -5,6 +5,8 @@ | |||
defmodule Pleroma.Web.MastodonAPI.AuthController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, only: [json_response: 3] | |||
alias Pleroma.User | |||
alias Pleroma.Web.OAuth.App | |||
alias Pleroma.Web.OAuth.Authorization | |||
@@ -61,9 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do | |||
TwitterAPI.password_reset(nickname_or_email) | |||
conn | |||
|> put_status(:no_content) | |||
|> json("") | |||
json_response(conn, :no_content, "") | |||
end | |||
defp local_mastodon_root_path(conn) do | |||
@@ -4,6 +4,8 @@ | |||
defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
use Pleroma.Web, :controller | |||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] | |||
alias Pleroma.Activity | |||
alias Pleroma.Chat | |||
alias Pleroma.Chat.MessageReference | |||
@@ -47,7 +49,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
}) do | |||
with %MessageReference{} = cm_ref <- | |||
MessageReference.get_by_id(message_id), | |||
^chat_id <- cm_ref.chat_id |> to_string(), | |||
^chat_id <- to_string(cm_ref.chat_id), | |||
%Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id), | |||
{:ok, _} <- remove_or_delete(cm_ref, user) do | |||
conn | |||
@@ -68,18 +70,13 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
end | |||
end | |||
defp remove_or_delete(cm_ref, _) do | |||
cm_ref | |||
|> MessageReference.delete() | |||
end | |||
defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref) | |||
def post_chat_message( | |||
%{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn, | |||
%{ | |||
id: id | |||
} | |||
%{body_params: params, assigns: %{user: user}} = conn, | |||
%{id: id} | |||
) do | |||
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id), | |||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id), | |||
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient), | |||
{:ok, activity} <- | |||
CommonAPI.post_chat_message(user, recipient, params[:content], | |||
@@ -103,13 +100,12 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
end | |||
end | |||
def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{ | |||
id: chat_id, | |||
message_id: message_id | |||
}) do | |||
with %MessageReference{} = cm_ref <- | |||
MessageReference.get_by_id(message_id), | |||
^chat_id <- cm_ref.chat_id |> to_string(), | |||
def mark_message_as_read( | |||
%{assigns: %{user: %{id: user_id}}} = conn, | |||
%{id: chat_id, message_id: message_id} | |||
) do | |||
with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id), | |||
^chat_id <- to_string(cm_ref.chat_id), | |||
%Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id), | |||
{:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do | |||
conn | |||
@@ -119,36 +115,28 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
end | |||
def mark_as_read( | |||
%{ | |||
body_params: %{last_read_id: last_read_id}, | |||
assigns: %{user: %{id: user_id}} | |||
} = conn, | |||
%{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn, | |||
%{id: id} | |||
) do | |||
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id), | |||
{_n, _} <- | |||
MessageReference.set_all_seen_for_chat(chat, last_read_id) do | |||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id), | |||
{_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do | |||
conn | |||
|> put_view(ChatView) | |||
|> render("show.json", chat: chat) | |||
end | |||
end | |||
def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do | |||
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do | |||
cm_refs = | |||
def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do | |||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do | |||
chat_message_refs = | |||
chat | |||
|> MessageReference.for_chat_query() | |||
|> Pagination.fetch_paginated(params) | |||
conn | |||
|> add_link_headers(chat_message_refs) | |||
|> put_view(MessageReferenceView) | |||
|> render("index.json", chat_message_references: cm_refs) | |||
else | |||
_ -> | |||
conn | |||
|> put_status(:not_found) | |||
|> json(%{error: "not found"}) | |||
|> render("index.json", chat_message_references: chat_message_refs) | |||
end | |||
end | |||
@@ -165,8 +153,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
|> render("index.json", chats: chats) | |||
end | |||
def create(%{assigns: %{user: user}} = conn, params) do | |||
with %User{ap_id: recipient} <- User.get_by_id(params[:id]), | |||
def create(%{assigns: %{user: user}} = conn, %{id: id}) do | |||
with %User{ap_id: recipient} <- User.get_cached_by_id(id), | |||
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do | |||
conn | |||
|> put_view(ChatView) | |||
@@ -174,8 +162,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do | |||
end | |||
end | |||
def show(%{assigns: %{user: user}} = conn, params) do | |||
with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do | |||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do | |||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do | |||
conn | |||
|> put_view(ChatView) | |||
|> render("show.json", chat: chat) | |||
@@ -0,0 +1,133 @@ | |||
defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||
use Pleroma.Web, :controller | |||
alias Pleroma.Emoji.Pack | |||
alias Pleroma.Web.ApiSpec | |||
plug(Pleroma.Web.ApiSpec.CastAndValidate) | |||
plug( | |||
Pleroma.Plugs.OAuthScopesPlug, | |||
%{scopes: ["write"], admin: true} | |||
when action in [ | |||
:create, | |||
:update, | |||
:delete | |||
] | |||
) | |||
defdelegate open_api_operation(action), to: ApiSpec.PleromaEmojiFileOperation | |||
def create(%{body_params: params} = conn, %{name: pack_name}) do | |||
filename = params[:filename] || get_filename(params[:file]) | |||
shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename)) | |||
with {:ok, pack} <- Pack.load_pack(pack_name), | |||
{:ok, file} <- get_file(params[:file]), | |||
{:ok, pack} <- Pack.add_file(pack, shortcode, filename, file) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :already_exists} -> | |||
conn | |||
|> put_status(:conflict) | |||
|> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:unprocessable_entity) | |||
|> json(%{error: "pack name, shortcode or filename cannot be empty"}) | |||
{:error, _} = error -> | |||
handle_error(conn, error, %{pack_name: pack_name}) | |||
end | |||
end | |||
def update(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: pack_name}) do | |||
new_shortcode = params[:new_shortcode] | |||
new_filename = params[:new_filename] | |||
force = params[:force] | |||
with {:ok, pack} <- Pack.load_pack(pack_name), | |||
{:ok, pack} <- Pack.update_file(pack, shortcode, new_shortcode, new_filename, force) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :already_exists} -> | |||
conn | |||
|> put_status(:conflict) | |||
|> json(%{ | |||
error: | |||
"New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" | |||
}) | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:unprocessable_entity) | |||
|> json(%{error: "new_shortcode or new_filename cannot be empty"}) | |||
{:error, _} = error -> | |||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) | |||
end | |||
end | |||
def delete(conn, %{name: pack_name, shortcode: shortcode}) do | |||
with {:ok, pack} <- Pack.load_pack(pack_name), | |||
{:ok, pack} <- Pack.delete_file(pack, shortcode) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:unprocessable_entity) | |||
|> json(%{error: "pack name or shortcode cannot be empty"}) | |||
{:error, _} = error -> | |||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) | |||
end | |||
end | |||
defp handle_error(conn, {:error, :doesnt_exist}, %{code: emoji_code}) do | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) | |||
end | |||
defp handle_error(conn, {:error, :not_found}, %{pack_name: pack_name}) do | |||
conn | |||
|> put_status(:not_found) | |||
|> json(%{error: "pack \"#{pack_name}\" is not found"}) | |||
end | |||
defp handle_error(conn, {:error, _}, _) do | |||
render_error( | |||
conn, | |||
:internal_server_error, | |||
"Unexpected error occurred while adding file to pack." | |||
) | |||
end | |||
defp get_filename(%Plug.Upload{filename: filename}), do: filename | |||
defp get_filename(url) when is_binary(url), do: Path.basename(url) | |||
def get_file(%Plug.Upload{} = file), do: {:ok, file} | |||
def get_file(url) when is_binary(url) do | |||
with {:ok, %Tesla.Env{body: body, status: code, headers: headers}} | |||
when code in 200..299 <- Pleroma.HTTP.get(url) do | |||
path = Plug.Upload.random_file!("emoji") | |||
content_type = | |||
case List.keyfind(headers, "content-type", 0) do | |||
{"content-type", value} -> value | |||
nil -> nil | |||
end | |||
File.write(path, body) | |||
{:ok, | |||
%Plug.Upload{ | |||
filename: Path.basename(url), | |||
path: path, | |||
content_type: content_type | |||
}} | |||
end | |||
end | |||
end |
@@ -14,10 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
:download, | |||
:create, | |||
:update, | |||
:delete, | |||
:add_file, | |||
:update_file, | |||
:delete_file | |||
:delete | |||
] | |||
) | |||
@@ -184,105 +181,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
end | |||
end | |||
def add_file(%{body_params: params} = conn, %{name: name}) do | |||
filename = params[:filename] || get_filename(params[:file]) | |||
shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename)) | |||
with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params[:file]) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :already_exists} -> | |||
conn | |||
|> put_status(:conflict) | |||
|> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"}) | |||
{:error, :not_found} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack \"#{name}\" is not found"}) | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack name, shortcode or filename cannot be empty"}) | |||
{:error, _} -> | |||
render_error( | |||
conn, | |||
:internal_server_error, | |||
"Unexpected error occurred while adding file to pack." | |||
) | |||
end | |||
end | |||
def update_file(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: name}) do | |||
new_shortcode = params[:new_shortcode] | |||
new_filename = params[:new_filename] | |||
force = params[:force] | |||
with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :doesnt_exist} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) | |||
{:error, :already_exists} -> | |||
conn | |||
|> put_status(:conflict) | |||
|> json(%{ | |||
error: | |||
"New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option" | |||
}) | |||
{:error, :not_found} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack \"#{name}\" is not found"}) | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "new_shortcode or new_filename cannot be empty"}) | |||
{:error, _} -> | |||
render_error( | |||
conn, | |||
:internal_server_error, | |||
"Unexpected error occurred while updating file in pack." | |||
) | |||
end | |||
end | |||
def delete_file(conn, %{name: name, shortcode: shortcode}) do | |||
with {:ok, pack} <- Pack.delete_file(name, shortcode) do | |||
json(conn, pack.files) | |||
else | |||
{:error, :doesnt_exist} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"}) | |||
{:error, :not_found} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack \"#{name}\" is not found"}) | |||
{:error, :empty_values} -> | |||
conn | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack name or shortcode cannot be empty"}) | |||
{:error, _} -> | |||
render_error( | |||
conn, | |||
:internal_server_error, | |||
"Unexpected error occurred while removing file from pack." | |||
) | |||
end | |||
end | |||
def import_from_filesystem(conn, _params) do | |||
with {:ok, names} <- Pack.import_from_filesystem() do | |||
json(conn, names) | |||
@@ -298,7 +196,4 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> json(%{error: "Error accessing emoji pack directory"}) | |||
end | |||
end | |||
defp get_filename(%Plug.Upload{filename: filename}), do: filename | |||
defp get_filename(url) when is_binary(url), do: Path.basename(url) | |||
end |
@@ -238,9 +238,9 @@ defmodule Pleroma.Web.Router do | |||
patch("/:name", EmojiPackController, :update) | |||
delete("/:name", EmojiPackController, :delete) | |||
post("/:name/files", EmojiPackController, :add_file) | |||
patch("/:name/files", EmojiPackController, :update_file) | |||
delete("/:name/files", EmojiPackController, :delete_file) | |||
post("/:name/files", EmojiFileController, :create) | |||
patch("/:name/files", EmojiFileController, :update) | |||
delete("/:name/files", EmojiFileController, :delete) | |||
end | |||
# Pack info / downloading | |||
@@ -0,0 +1,19 @@ | |||
defmodule Pleroma.Repo.Migrations.MakeUserIdsCI do | |||
use Ecto.Migration | |||
def up do | |||
alter table(:users) do | |||
modify(:uri, :citext) | |||
end | |||
create(unique_index(:users, :uri)) | |||
end | |||
def don do | |||
drop(unique_index(:users, :uri)) | |||
alter table(:users) do | |||
modify(:uri, :text) | |||
end | |||
end | |||
end |
@@ -0,0 +1,93 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Emoji.PackTest do | |||
use ExUnit.Case, async: true | |||
alias Pleroma.Emoji.Pack | |||
@emoji_path Path.join( | |||
Pleroma.Config.get!([:instance, :static_dir]), | |||
"emoji" | |||
) | |||
setup do | |||
pack_path = Path.join(@emoji_path, "dump_pack") | |||
File.mkdir(pack_path) | |||
File.write!(Path.join(pack_path, "pack.json"), """ | |||
{ | |||
"files": { }, | |||
"pack": { | |||
"description": "Dump pack", "homepage": "https://pleroma.social", | |||
"license": "Test license", "share-files": true | |||
}} | |||
""") | |||
{:ok, pack} = Pleroma.Emoji.Pack.load_pack("dump_pack") | |||
on_exit(fn -> | |||
File.rm_rf!(pack_path) | |||
end) | |||
{:ok, pack: pack} | |||
end | |||
describe "add_file/4" do | |||
test "add emojies from zip file", %{pack: pack} do | |||
file = %Plug.Upload{ | |||
content_type: "application/zip", | |||
filename: "emojis.zip", | |||
path: Path.absname("test/fixtures/emojis.zip") | |||
} | |||
{:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) | |||
assert updated_pack.files == %{ | |||
"a_trusted_friend-128" => "128px/a_trusted_friend-128.png", | |||
"auroraborealis" => "auroraborealis.png", | |||
"baby_in_a_box" => "1000px/baby_in_a_box.png", | |||
"bear" => "1000px/bear.png", | |||
"bear-128" => "128px/bear-128.png" | |||
} | |||
assert updated_pack.files_count == 5 | |||
end | |||
end | |||
test "returns error when zip file is bad", %{pack: pack} do | |||
file = %Plug.Upload{ | |||
content_type: "application/zip", | |||
filename: "emojis.zip", | |||
path: Path.absname("test/instance_static/emoji/test_pack/blank.png") | |||
} | |||
assert Pack.add_file(pack, nil, nil, file) == {:error, :einval} | |||
end | |||
test "returns pack when zip file is empty", %{pack: pack} do | |||
file = %Plug.Upload{ | |||
content_type: "application/zip", | |||
filename: "emojis.zip", | |||
path: Path.absname("test/fixtures/empty.zip") | |||
} | |||
{:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) | |||
assert updated_pack == pack | |||
end | |||
test "add emoji file", %{pack: pack} do | |||
file = %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
{:ok, updated_pack} = Pack.add_file(pack, "test_blank", "test_blank.png", file) | |||
assert updated_pack.files == %{ | |||
"test_blank" => "test_blank.png" | |||
} | |||
assert updated_pack.files_count == 1 | |||
end | |||
end |
@@ -3,7 +3,7 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.EmojiTest do | |||
use ExUnit.Case, async: true | |||
use ExUnit.Case | |||
alias Pleroma.Emoji | |||
describe "is_unicode_emoji?/1" do | |||
@@ -0,0 +1,37 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.User.QueryTest do | |||
use Pleroma.DataCase, async: true | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
alias Pleroma.User.Query | |||
alias Pleroma.Web.ActivityPub.InternalFetchActor | |||
import Pleroma.Factory | |||
describe "internal users" do | |||
test "it filters out internal users by default" do | |||
%User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() | |||
assert [_user] = User |> Repo.all() | |||
assert [] == %{} |> Query.build() |> Repo.all() | |||
end | |||
test "it filters out users without nickname by default" do | |||
insert(:user, %{nickname: nil}) | |||
assert [_user] = User |> Repo.all() | |||
assert [] == %{} |> Query.build() |> Repo.all() | |||
end | |||
test "it returns internal users when enabled" do | |||
%User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() | |||
insert(:user, %{nickname: nil}) | |||
assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 | |||
end | |||
end | |||
end |
@@ -17,6 +17,40 @@ defmodule Pleroma.UserSearchTest do | |||
describe "User.search" do | |||
setup do: clear_config([:instance, :limit_to_local_content]) | |||
test "returns a resolved user as the first result" do | |||
Pleroma.Config.put([:instance, :limit_to_local_content], false) | |||
user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) | |||
_user = insert(:user, %{nickname: "com_user"}) | |||
[first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true) | |||
assert first_user.id == user.id | |||
end | |||
test "returns a user with matching ap_id as the first result" do | |||
user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) | |||
_user = insert(:user, %{nickname: "com_user"}) | |||
[first_user, _second_user] = User.search("https://lain.com/users/lain") | |||
assert first_user.id == user.id | |||
end | |||
test "returns a user with matching uri as the first result" do | |||
user = | |||
insert(:user, %{ | |||
nickname: "no_relation", | |||
ap_id: "https://lain.com/users/lain", | |||
uri: "https://lain.com/@Lain" | |||
}) | |||
_user = insert(:user, %{nickname: "com_user"}) | |||
[first_user, _second_user] = User.search("https://lain.com/@lain") | |||
assert first_user.id == user.id | |||
end | |||
test "excludes invisible users from results" do | |||
user = insert(:user, %{nickname: "john t1000"}) | |||
insert(:user, %{invisible: true, nickname: "john t800"}) | |||
@@ -509,7 +509,12 @@ defmodule Pleroma.UserTest do | |||
cng = User.register_changeset(%User{}, @full_user_data) | |||
{:ok, registered_user} = User.register(cng) | |||
ObanHelpers.perform_all() | |||
assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(registered_user)) | |||
Pleroma.Emails.UserEmail.account_confirmation_email(registered_user) | |||
# temporary hackney fix until hackney max_connections bug is fixed | |||
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101 | |||
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) | |||
|> assert_email_sent() | |||
end | |||
test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do | |||
@@ -0,0 +1,15 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.UtilsTest do | |||
use ExUnit.Case, async: true | |||
describe "tmp_dir/1" do | |||
test "returns unique temporary directory" do | |||
{:ok, path} = Pleroma.Utils.tmp_dir("emoji") | |||
assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ | |||
File.rm_rf(path) | |||
end | |||
end | |||
end |
@@ -157,12 +157,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do | |||
} | |||
end | |||
test "returns an error if received a second time" do | |||
test "returns same activity if received a second time" do | |||
data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() | |||
assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) | |||
assert {:error, {:validate_object, {:error, _}}} = Transmogrifier.handle_incoming(data) | |||
assert {:ok, ^activity} = Transmogrifier.handle_incoming(data) | |||
end | |||
test "accepts a Question with no content" do | |||
@@ -1977,7 +1977,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
}" | |||
ObanHelpers.perform_all() | |||
assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(first_user)) | |||
Pleroma.Emails.UserEmail.account_confirmation_email(first_user) | |||
# temporary hackney fix until hackney max_connections bug is fixed | |||
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101 | |||
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) | |||
|> assert_email_sent() | |||
end | |||
end | |||
@@ -29,6 +29,23 @@ defmodule Pleroma.Web.CommonAPITest do | |||
setup do: clear_config([:instance, :limit]) | |||
setup do: clear_config([:instance, :max_pinned_statuses]) | |||
describe "posting polls" do | |||
test "it posts a poll" do | |||
user = insert(:user) | |||
{:ok, activity} = | |||
CommonAPI.post(user, %{ | |||
status: "who is the best", | |||
poll: %{expires_in: 600, options: ["reimu", "marisa"]} | |||
}) | |||
object = Object.normalize(activity) | |||
assert object.data["type"] == "Question" | |||
assert object.data["oneOf"] |> length() == 2 | |||
end | |||
end | |||
describe "blocking" do | |||
setup do | |||
blocker = insert(:user) | |||
@@ -61,7 +61,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do | |||
end | |||
test "it returns 204", %{conn: conn} do | |||
assert json_response(conn, :no_content) | |||
assert empty_json_response(conn) | |||
end | |||
test "it creates a PasswordResetToken record for user", %{user: user} do | |||
@@ -91,7 +91,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do | |||
assert conn | |||
|> post("/auth/password?nickname=#{user.nickname}") | |||
|> json_response(:no_content) | |||
|> empty_json_response() | |||
ObanHelpers.perform_all() | |||
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) | |||
@@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do | |||
assert conn | |||
|> post("/auth/password?nickname=#{user.nickname}") | |||
|> json_response(:no_content) | |||
|> empty_json_response() | |||
end | |||
end | |||
@@ -125,24 +125,21 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do | |||
test "it returns 204 when user is not found", %{conn: conn, user: user} do | |||
conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") | |||
assert conn | |||
|> json_response(:no_content) | |||
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 conn | |||
|> json_response(:no_content) | |||
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, deactivated: true, local: true)) | |||
conn = post(conn, "/auth/password?email=#{user.email}") | |||
assert conn | |||
|> json_response(:no_content) | |||
assert empty_json_response(conn) | |||
end | |||
end | |||
@@ -2,7 +2,7 @@ | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do | |||
use Pleroma.Web.ConnCase, async: true | |||
use Pleroma.Web.ConnCase | |||
alias Pleroma.Chat | |||
alias Pleroma.Chat.MessageReference | |||
@@ -201,17 +201,39 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do | |||
chat = Chat.get(user.id, recipient.ap_id) | |||
result = | |||
conn | |||
|> get("/api/v1/pleroma/chats/#{chat.id}/messages") | |||
|> json_response_and_validate_schema(200) | |||
response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages") | |||
result = json_response_and_validate_schema(response, 200) | |||
[next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ") | |||
api_endpoint = "/api/v1/pleroma/chats/" | |||
assert String.match?( | |||
next, | |||
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) | |||
) | |||
assert String.match?( | |||
prev, | |||
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$) | |||
) | |||
assert length(result) == 20 | |||
result = | |||
conn | |||
|> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") | |||
|> json_response_and_validate_schema(200) | |||
response = | |||
get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") | |||
result = json_response_and_validate_schema(response, 200) | |||
[next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ") | |||
assert String.match?( | |||
next, | |||
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) | |||
) | |||
assert String.match?( | |||
prev, | |||
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$) | |||
) | |||
assert length(result) == 10 | |||
end | |||
@@ -240,12 +262,10 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do | |||
assert length(result) == 3 | |||
# Trying to get the chat of a different user | |||
result = | |||
conn | |||
|> assign(:user, other_user) | |||
|> get("/api/v1/pleroma/chats/#{chat.id}/messages") | |||
assert result |> json_response(404) | |||
conn | |||
|> assign(:user, other_user) | |||
|> get("/api/v1/pleroma/chats/#{chat.id}/messages") | |||
|> json_response_and_validate_schema(404) | |||
end | |||
end | |||
@@ -0,0 +1,357 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do | |||
use Pleroma.Web.ConnCase | |||
import Tesla.Mock | |||
import Pleroma.Factory | |||
@emoji_path Path.join( | |||
Pleroma.Config.get!([:instance, :static_dir]), | |||
"emoji" | |||
) | |||
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) | |||
setup do: clear_config([:instance, :public], true) | |||
setup do | |||
admin = insert(:user, is_admin: true) | |||
token = insert(:oauth_admin_token, user: admin) | |||
admin_conn = | |||
build_conn() | |||
|> assign(:user, admin) | |||
|> assign(:token, token) | |||
Pleroma.Emoji.reload() | |||
{:ok, %{admin_conn: admin_conn}} | |||
end | |||
describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do | |||
setup do | |||
pack_file = "#{@emoji_path}/test_pack/pack.json" | |||
original_content = File.read!(pack_file) | |||
on_exit(fn -> | |||
File.write!(pack_file, original_content) | |||
end) | |||
:ok | |||
end | |||
test "upload zip file with emojies", %{admin_conn: admin_conn} do | |||
on_exit(fn -> | |||
[ | |||
"128px/a_trusted_friend-128.png", | |||
"auroraborealis.png", | |||
"1000px/baby_in_a_box.png", | |||
"1000px/bear.png", | |||
"128px/bear-128.png" | |||
] | |||
|> Enum.each(fn path -> File.rm_rf!("#{@emoji_path}/test_pack/#{path}") end) | |||
end) | |||
resp = | |||
admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
file: %Plug.Upload{ | |||
content_type: "application/zip", | |||
filename: "emojis.zip", | |||
path: Path.absname("test/fixtures/emojis.zip") | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) | |||
assert resp == %{ | |||
"a_trusted_friend-128" => "128px/a_trusted_friend-128.png", | |||
"auroraborealis" => "auroraborealis.png", | |||
"baby_in_a_box" => "1000px/baby_in_a_box.png", | |||
"bear" => "1000px/bear.png", | |||
"bear-128" => "128px/bear-128.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
Enum.each(Map.values(resp), fn path -> | |||
assert File.exists?("#{@emoji_path}/test_pack/#{path}") | |||
end) | |||
end | |||
test "create shortcode exists", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(:conflict) == %{ | |||
"error" => "An emoji with the \"blank\" shortcode already exists" | |||
} | |||
end | |||
test "don't rewrite old emoji", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank3" => "dir/blank.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
new_shortcode: "blank2", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:conflict) == %{ | |||
"error" => | |||
"New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" | |||
} | |||
end | |||
test "rewrite old emoji with force option", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank3" => "dir/blank.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
new_shortcode: "blank4", | |||
new_filename: "dir_2/blank_3.png", | |||
force: true | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank4" => "dir_2/blank_3.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") | |||
end | |||
test "with empty filename", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank2", | |||
filename: "", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(422) == %{ | |||
"error" => "pack name, shortcode or filename cannot be empty" | |||
} | |||
end | |||
test "add file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/not_loaded/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(:not_found) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "remove file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(:not_found) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "remove file with empty shortcode", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") | |||
|> json_response_and_validate_schema(:not_found) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "update file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ | |||
shortcode: "blank4", | |||
new_shortcode: "blank3", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:not_found) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "new with shortcode as file with update", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank4", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank4" => "dir/blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank4", | |||
new_shortcode: "blank3", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank3" => "dir_2/blank_3.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
refute File.exists?("#{@emoji_path}/test_pack/dir/") | |||
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
refute File.exists?("#{@emoji_path}/test_pack/dir_2/") | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) | |||
end | |||
test "new with shortcode from url", %{admin_conn: admin_conn} do | |||
mock(fn | |||
%{ | |||
method: :get, | |||
url: "https://test-blank/blank_url.png" | |||
} -> | |||
text(File.read!("#{@emoji_path}/test_pack/blank.png")) | |||
end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank_url", | |||
file: "https://test-blank/blank_url.png" | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank_url" => "blank_url.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) | |||
end | |||
test "new without shortcode", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
file: %Plug.Upload{ | |||
filename: "shortcode.png", | |||
path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"shortcode" => "shortcode.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
end | |||
test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "Emoji \"blank3\" does not exist" | |||
} | |||
end | |||
test "update non existing emoji", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
new_shortcode: "blank4", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "Emoji \"blank3\" does not exist" | |||
} | |||
end | |||
test "update with empty shortcode", %{admin_conn: admin_conn} do | |||
assert %{ | |||
"error" => "Missing field: new_shortcode." | |||
} = | |||
admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) | |||
end | |||
end | |||
end |
@@ -411,293 +411,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do | |||
end | |||
end | |||
describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do | |||
setup do | |||
pack_file = "#{@emoji_path}/test_pack/pack.json" | |||
original_content = File.read!(pack_file) | |||
on_exit(fn -> | |||
File.write!(pack_file, original_content) | |||
end) | |||
:ok | |||
end | |||
test "create shortcode exists", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(:conflict) == %{ | |||
"error" => "An emoji with the \"blank\" shortcode already exists" | |||
} | |||
end | |||
test "don't rewrite old emoji", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank3" => "dir/blank.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
new_shortcode: "blank2", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:conflict) == %{ | |||
"error" => | |||
"New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" | |||
} | |||
end | |||
test "rewrite old emoji with force option", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank3" => "dir/blank.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
new_shortcode: "blank4", | |||
new_filename: "dir_2/blank_3.png", | |||
force: true | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png", | |||
"blank4" => "dir_2/blank_3.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") | |||
end | |||
test "with empty filename", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank2", | |||
filename: "", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "pack name, shortcode or filename cannot be empty" | |||
} | |||
end | |||
test "add file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/not_loaded/files", %{ | |||
shortcode: "blank3", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "remove file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "remove file with empty shortcode", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "pack name or shortcode cannot be empty" | |||
} | |||
end | |||
test "update file with not loaded pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ | |||
shortcode: "blank4", | |||
new_shortcode: "blank3", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "pack \"not_loaded\" is not found" | |||
} | |||
end | |||
test "new with shortcode as file with update", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank4", | |||
filename: "dir/blank.png", | |||
file: %Plug.Upload{ | |||
filename: "blank.png", | |||
path: "#{@emoji_path}/test_pack/blank.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank4" => "dir/blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank4", | |||
new_shortcode: "blank3", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank3" => "dir_2/blank_3.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
refute File.exists?("#{@emoji_path}/test_pack/dir/") | |||
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
refute File.exists?("#{@emoji_path}/test_pack/dir_2/") | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) | |||
end | |||
test "new with shortcode from url", %{admin_conn: admin_conn} do | |||
mock(fn | |||
%{ | |||
method: :get, | |||
url: "https://test-blank/blank_url.png" | |||
} -> | |||
text(File.read!("#{@emoji_path}/test_pack/blank.png")) | |||
end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank_url", | |||
file: "https://test-blank/blank_url.png" | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"blank_url" => "blank_url.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) | |||
end | |||
test "new without shortcode", %{admin_conn: admin_conn} do | |||
on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
file: %Plug.Upload{ | |||
filename: "shortcode.png", | |||
path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" | |||
} | |||
}) | |||
|> json_response_and_validate_schema(200) == %{ | |||
"shortcode" => "shortcode.png", | |||
"blank" => "blank.png", | |||
"blank2" => "blank2.png" | |||
} | |||
end | |||
test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "Emoji \"blank3\" does not exist" | |||
} | |||
end | |||
test "update non existing emoji", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank3", | |||
new_shortcode: "blank4", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) == %{ | |||
"error" => "Emoji \"blank3\" does not exist" | |||
} | |||
end | |||
test "update with empty shortcode", %{admin_conn: admin_conn} do | |||
assert %{ | |||
"error" => "Missing field: new_shortcode." | |||
} = | |||
admin_conn | |||
|> put_req_header("content-type", "multipart/form-data") | |||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{ | |||
shortcode: "blank", | |||
new_filename: "dir_2/blank_3.png" | |||
}) | |||
|> json_response_and_validate_schema(:bad_request) | |||
end | |||
end | |||
describe "POST/DELETE /api/pleroma/emoji/packs/:name" do | |||
test "creating and deleting a pack", %{admin_conn: admin_conn} do | |||
assert admin_conn | |||