Merge branch 'develop' into fix/deprecation-warning-welcome-message

This commit is contained in:
Mark Felder 2020-09-25 10:48:01 -05:00
commit 2bf2c68dee
35 changed files with 1168 additions and 634 deletions

View file

@ -5,11 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased ## Unreleased
### Added
- Experimental websocket-based federation between Pleroma instances.
### Changed ### 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 `: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. - 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. - 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 ### Added
- Media preview proxy (requires media proxy be enabled; see `:media_preview_proxy` config for more details). - 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. - 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 ### 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 - 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. 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 ## [2.1.2] - 2020-09-17

View file

@ -18,15 +18,16 @@ If you are running Linux (glibc or musl) on x86/arm, the recommended way to inst
### From Source ### 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. 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/) - [Alpine Linux](https://docs-develop.pleroma.social/backend/installation/alpine_linux_en/)
- [Arch Linux](https://docs-develop.pleroma.social/backend/installation/arch_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/) - [Gentoo Linux](https://docs-develop.pleroma.social/backend/installation/gentoo_en/)
- [NetBSD](https://docs-develop.pleroma.social/backend/installation/netbsd_en/) - [NetBSD](https://docs-develop.pleroma.social/backend/installation/netbsd_en/)
- [OpenBSD](https://docs-develop.pleroma.social/backend/installation/openbsd_en/) - [OpenBSD](https://docs-develop.pleroma.social/backend/installation/openbsd_en/)
- [OpenBSD (fi)](https://docs-develop.pleroma.social/backend/installation/openbsd_fi/) - [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 ### 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**. 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**.

View file

@ -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. 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} @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
schema "chats" do schema "chats" do
@ -41,16 +42,28 @@ def changeset(struct, params) do
|> unique_constraint(:user_id, name: :chats_user_id_recipient_index) |> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
end 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 def get_by_id(id) do
__MODULE__ Repo.get(__MODULE__, id)
|> Repo.get(id)
end end
@spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil
def get(user_id, recipient) do def get(user_id, recipient) do
__MODULE__ Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient)
|> Repo.get_by(user_id: user_id, recipient: recipient)
end 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 def get_or_create(user_id, recipient) do
%__MODULE__{} %__MODULE__{}
|> changeset(%{user_id: user_id, recipient: recipient}) |> changeset(%{user_id: user_id, recipient: recipient})
@ -62,6 +75,8 @@ def get_or_create(user_id, recipient) do
) )
end 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 def bump_or_create(user_id, recipient) do
%__MODULE__{} %__MODULE__{}
|> changeset(%{user_id: user_id, recipient: recipient}) |> changeset(%{user_id: user_id, recipient: recipient})

View file

@ -35,6 +35,11 @@ def perform(:deliver_async, email, config), do: deliver(email, config)
def deliver(email, config \\ []) def deliver(email, config \\ [])
def deliver(email, config) do 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 case enabled?() do
true -> Swoosh.Mailer.deliver(email, parse_config(config)) true -> Swoosh.Mailer.deliver(email, parse_config(config))
false -> {:error, :deliveries_disabled} false -> {:error, :deliveries_disabled}

View file

@ -56,6 +56,9 @@ def get(name) do
end end
end end
@spec exist?(String.t()) :: boolean()
def exist?(name), do: not is_nil(get(name))
@doc "Returns all the emojos!!" @doc "Returns all the emojos!!"
@spec get_all() :: list({String.t(), String.t(), String.t()}) @spec get_all() :: list({String.t(), String.t(), String.t()})
def get_all do def get_all do

View file

@ -17,6 +17,7 @@ defmodule Pleroma.Emoji.Pack do
} }
alias Pleroma.Emoji alias Pleroma.Emoji
alias Pleroma.Emoji.Pack
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} @spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
def create(name) do def create(name) do
@ -64,24 +65,93 @@ def delete(name) do
end end
end end
@spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) :: @spec unpack_zip_emojies(list(tuple())) :: list(map())
{:ok, t()} | {:error, File.posix() | atom()} defp unpack_zip_emojies(zip_files) do
def add_file(name, shortcode, filename, file) do Enum.reduce(zip_files, [], fn
with :ok <- validate_not_empty([name, shortcode, filename]), {_, 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 <- validate_emoji_not_exists(shortcode),
{:ok, pack} <- load_pack(name), {:ok, updated_pack} <- do_add_file(pack, shortcode, filename, file) do
:ok <- save_file(file, pack, filename),
{:ok, updated_pack} <- pack |> put_emoji(shortcode, filename) |> save_pack() do
Emoji.reload() Emoji.reload()
{:ok, updated_pack} {:ok, updated_pack}
end end
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()} {:ok, t()} | {:error, File.posix() | atom()}
def delete_file(name, shortcode) do def delete_file(%Pack{} = pack, shortcode) do
with :ok <- validate_not_empty([name, shortcode]), with :ok <- validate_not_empty([shortcode]),
{:ok, pack} <- load_pack(name),
:ok <- remove_file(pack, shortcode), :ok <- remove_file(pack, shortcode),
{:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do {:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do
Emoji.reload() Emoji.reload()
@ -89,11 +159,10 @@ def delete_file(name, shortcode) do
end end
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()} {:ok, t()} | {:error, File.posix() | atom()}
def update_file(name, shortcode, new_shortcode, new_filename, force) do def update_file(%Pack{} = pack, shortcode, new_shortcode, new_filename, force) do
with :ok <- validate_not_empty([name, shortcode, new_shortcode, new_filename]), with :ok <- validate_not_empty([shortcode, new_shortcode, new_filename]),
{:ok, pack} <- load_pack(name),
{:ok, filename} <- get_filename(pack, shortcode), {:ok, filename} <- get_filename(pack, shortcode),
:ok <- validate_emoji_not_exists(new_shortcode, force), :ok <- validate_emoji_not_exists(new_shortcode, force),
:ok <- rename_file(pack, filename, new_filename), :ok <- rename_file(pack, filename, new_filename),
@ -243,9 +312,10 @@ defp validate_emoji_not_exists(shortcode, force \\ false)
defp validate_emoji_not_exists(_shortcode, true), do: :ok defp validate_emoji_not_exists(_shortcode, true), do: :ok
defp validate_emoji_not_exists(shortcode, _) do defp validate_emoji_not_exists(shortcode, _) do
case Emoji.get(shortcode) do if Emoji.exist?(shortcode) do
nil -> :ok {:error, :already_exists}
_ -> {:error, :already_exists} else
:ok
end end
end end
@ -386,25 +456,18 @@ defp validate_not_empty(list) do
end end
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) file_path = Path.join(pack.path, filename)
create_subdirs(file_path) create_subdirs(file_path)
case file do with {:ok, _} <- File.copy(upload_path, file_path) do
%Plug.Upload{path: upload_path} -> :ok
# 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)
end end
end end
defp put_emoji(pack, shortcode, filename) do defp put_emoji(pack, shortcode, filename) do
files = Map.put(pack.files, shortcode, filename) files = Map.put(pack.files, shortcode, filename)
%{pack | files: files} %{pack | files: files, files_count: length(Map.keys(files))}
end end
defp delete_emoji(pack, shortcode) do defp delete_emoji(pack, shortcode) do

View file

@ -47,6 +47,7 @@ defmodule Pleroma.User.Query do
is_moderator: boolean(), is_moderator: boolean(),
super_users: boolean(), super_users: boolean(),
invisible: boolean(), invisible: boolean(),
internal: boolean(),
followers: User.t(), followers: User.t(),
friends: User.t(), friends: User.t(),
recipients_from_activity: [String.t()], recipients_from_activity: [String.t()],
@ -80,7 +81,9 @@ defp base_query do
end end
defp prepare_query(query, criteria) do 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 end
defp compose_query({key, value}, query) defp compose_query({key, value}, query)
@ -129,14 +132,12 @@ defp compose_query({:external, _}, query), do: location_query(query, false)
defp compose_query({:active, _}, query) do defp compose_query({:active, _}, query) do
User.restrict_deactivated(query) User.restrict_deactivated(query)
|> where([u], not is_nil(u.nickname))
|> where([u], u.approval_pending == false) |> where([u], u.approval_pending == false)
end end
defp compose_query({:legacy_active, _}, query) do defp compose_query({:legacy_active, _}, query) do
query query
|> where([u], fragment("not (?->'deactivated' @> 'true')", u.info)) |> where([u], fragment("not (?->'deactivated' @> 'true')", u.info))
|> where([u], not is_nil(u.nickname))
end end
defp compose_query({:deactivated, false}, query) do defp compose_query({:deactivated, false}, query) do
@ -145,7 +146,6 @@ defp compose_query({:deactivated, false}, query) do
defp compose_query({:deactivated, true}, query) do defp compose_query({:deactivated, true}, query) do
where(query, [u], u.deactivated == ^true) where(query, [u], u.deactivated == ^true)
|> where([u], not is_nil(u.nickname))
end end
defp compose_query({:need_approval, _}, query) do defp compose_query({:need_approval, _}, query) do
@ -199,10 +199,15 @@ defp compose_query({:limit, limit}, query) do
limit(query, ^limit) limit(query, ^limit)
end 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 compose_query(_unsupported_param, query), do: query
defp location_query(query, local) do defp location_query(query, local) do
where(query, [u], u.local == ^local) where(query, [u], u.local == ^local)
|> where([u], not is_nil(u.nickname))
end end
end end

View file

@ -3,8 +3,10 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do defmodule Pleroma.User.Search do
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.User alias Pleroma.User
import Ecto.Query import Ecto.Query
@limit 20 @limit 20
@ -19,16 +21,46 @@ def search(query_string, opts \\ []) do
query_string = format_query(query_string) 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 = results =
query_string query_string
|> search_query(for_user, following) |> search_query(for_user, following, top_user_ids)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
results results
end 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 defp format_query(query_string) do
# Strip the beginning @ off if there is a query # Strip the beginning @ off if there is a query
query_string = String.trim_leading(query_string, "@") query_string = String.trim_leading(query_string, "@")
@ -47,7 +79,7 @@ defp format_query(query_string) do
end end
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 for_user
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user) |> filter_blocked_user(for_user)
@ -56,13 +88,20 @@ defp search_query(query_string, for_user, following) do
|> filter_internal_users() |> filter_internal_users()
|> filter_blocked_domains(for_user) |> filter_blocked_domains(for_user)
|> fts_search(query_string) |> fts_search(query_string)
|> select_top_users(top_user_ids)
|> trigram_rank(query_string) |> trigram_rank(query_string)
|> boost_search_rank(for_user) |> boost_search_rank(for_user, top_user_ids)
|> subquery() |> subquery()
|> order_by(desc: :search_rank) |> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user) |> maybe_restrict_local(for_user)
end 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 defp fts_search(query, query_string) do
query_string = to_tsquery(query_string) query_string = to_tsquery(query_string)
@ -180,7 +219,7 @@ defp restrict_local(q), do: where(q, [u], u.local == true)
defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) 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) friends_ids = User.get_friends_ids(for_user)
followers_ids = User.get_followers_ids(for_user) followers_ids = User.get_followers_ids(for_user)
@ -192,6 +231,7 @@ defp boost_search_rank(query, %User{} = for_user) do
CASE WHEN (?) THEN (?) * 1.5 CASE WHEN (?) THEN (?) * 1.5
WHEN (?) THEN (?) * 1.3 WHEN (?) THEN (?) * 1.3
WHEN (?) THEN (?) * 1.1 WHEN (?) THEN (?) * 1.1
WHEN (?) THEN 9001
ELSE (?) END ELSE (?) END
""", """,
u.id in ^friends_ids and u.id in ^followers_ids, u.id in ^friends_ids and u.id in ^followers_ids,
@ -200,11 +240,26 @@ defp boost_search_rank(query, %User{} = for_user) do
u.search_rank, u.search_rank,
u.id in ^followers_ids, u.id in ^followers_ids,
u.search_rank, u.search_rank,
u.id in ^top_user_ids,
u.search_rank u.search_rank
) )
} }
) )
end 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 end

View file

@ -24,4 +24,24 @@ def compile_dir(dir) when is_binary(dir) do
def command_available?(command) do def command_available?(command) do
match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"])) match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"]))
end 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 end

View file

@ -515,15 +515,19 @@ def handle_incoming(
end end
def handle_incoming( def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype}} = data, %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
_options _options
) )
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do
data = Map.put(data, "object", strip_internal_fields(data["object"])) data = Map.put(data, "object", strip_internal_fields(data["object"]))
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), 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, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity} {:ok, activity}
else
%Activity{} = activity -> {:ok, activity}
e -> e
end end
end end

View file

@ -158,7 +158,8 @@ def messages_operation do
"The messages in the chat", "The messages in the chat",
"application/json", "application/json",
chat_messages_response() chat_messages_response()
) ),
404 => Operation.response("Not Found", "application/json", ApiError)
}, },
security: [ security: [
%{ %{

View file

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

View file

@ -175,111 +175,6 @@ def update_operation do
} }
end 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 def import_from_filesystem_operation do
%Operation{ %Operation{
tags: ["Emoji Packs"], tags: ["Emoji Packs"],

View file

@ -48,13 +48,13 @@ defp param_to_integer(val, default) when is_binary(val) do
defp param_to_integer(_, default), do: default 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 do: conn
def add_link_headers(conn, activities, extra_params) do def add_link_headers(conn, entries, extra_params) do
case get_pagination_fields(conn, activities, extra_params) do case get_pagination_fields(conn, entries, extra_params) do
%{"next" => next_url, "prev" => prev_url} -> %{"next" => next_url, "prev" => prev_url} ->
put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
@ -78,19 +78,15 @@ defp build_pagination_fields(conn, min_id, max_id, extra_params) do
} }
end end
def get_pagination_fields(conn, activities, extra_params \\ %{}) do def get_pagination_fields(conn, entries, extra_params \\ %{}) do
case List.last(activities) do case List.last(entries) do
%{pagination_id: max_id} when not is_nil(max_id) -> %{pagination_id: max_id} when not is_nil(max_id) ->
%{pagination_id: min_id} = %{pagination_id: min_id} = List.first(entries)
activities
|> List.first()
build_pagination_fields(conn, min_id, max_id, extra_params) build_pagination_fields(conn, min_id, max_id, extra_params)
%{id: max_id} -> %{id: max_id} ->
%{id: min_id} = %{id: min_id} = List.first(entries)
activities
|> List.first()
build_pagination_fields(conn, min_id, max_id, extra_params) build_pagination_fields(conn, min_id, max_id, extra_params)

View file

@ -5,6 +5,8 @@
defmodule Pleroma.Web.MastodonAPI.AuthController do defmodule Pleroma.Web.MastodonAPI.AuthController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Authorization
@ -61,9 +63,7 @@ def password_reset(conn, params) do
TwitterAPI.password_reset(nickname_or_email) TwitterAPI.password_reset(nickname_or_email)
conn json_response(conn, :no_content, "")
|> put_status(:no_content)
|> json("")
end end
defp local_mastodon_root_path(conn) do defp local_mastodon_root_path(conn) do

View file

@ -4,6 +4,8 @@
defmodule Pleroma.Web.PleromaAPI.ChatController do defmodule Pleroma.Web.PleromaAPI.ChatController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Chat alias Pleroma.Chat
alias Pleroma.Chat.MessageReference alias Pleroma.Chat.MessageReference
@ -47,7 +49,7 @@ def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
}) do }) do
with %MessageReference{} = cm_ref <- with %MessageReference{} = cm_ref <-
MessageReference.get_by_id(message_id), 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), %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
{:ok, _} <- remove_or_delete(cm_ref, user) do {:ok, _} <- remove_or_delete(cm_ref, user) do
conn conn
@ -68,18 +70,13 @@ defp remove_or_delete(
end end
end end
defp remove_or_delete(cm_ref, _) do defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
cm_ref
|> MessageReference.delete()
end
def post_chat_message( def post_chat_message(
%{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn, %{body_params: params, assigns: %{user: user}} = conn,
%{ %{id: id}
id: id
}
) do ) 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), %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
{:ok, activity} <- {:ok, activity} <-
CommonAPI.post_chat_message(user, recipient, params[:content], CommonAPI.post_chat_message(user, recipient, params[:content],
@ -103,13 +100,12 @@ def post_chat_message(
end end
end end
def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{ def mark_message_as_read(
id: chat_id, %{assigns: %{user: %{id: user_id}}} = conn,
message_id: message_id %{id: chat_id, message_id: message_id}
}) do ) do
with %MessageReference{} = cm_ref <- with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
MessageReference.get_by_id(message_id), ^chat_id <- to_string(cm_ref.chat_id),
^chat_id <- cm_ref.chat_id |> to_string(),
%Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id), %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
{:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
conn conn
@ -119,36 +115,28 @@ def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{
end end
def mark_as_read( def mark_as_read(
%{ %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
body_params: %{last_read_id: last_read_id},
assigns: %{user: %{id: user_id}}
} = conn,
%{id: id} %{id: id}
) do ) 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),
{_n, _} <- {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
MessageReference.set_all_seen_for_chat(chat, last_read_id) do
conn conn
|> put_view(ChatView) |> put_view(ChatView)
|> render("show.json", chat: chat) |> render("show.json", chat: chat)
end end
end end
def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
cm_refs = chat_message_refs =
chat chat
|> MessageReference.for_chat_query() |> MessageReference.for_chat_query()
|> Pagination.fetch_paginated(params) |> Pagination.fetch_paginated(params)
conn conn
|> add_link_headers(chat_message_refs)
|> put_view(MessageReferenceView) |> put_view(MessageReferenceView)
|> render("index.json", chat_message_references: cm_refs) |> render("index.json", chat_message_references: chat_message_refs)
else
_ ->
conn
|> put_status(:not_found)
|> json(%{error: "not found"})
end end
end end
@ -165,8 +153,8 @@ def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
|> render("index.json", chats: chats) |> render("index.json", chats: chats)
end end
def create(%{assigns: %{user: user}} = conn, params) do def create(%{assigns: %{user: user}} = conn, %{id: id}) do
with %User{ap_id: recipient} <- User.get_by_id(params[:id]), with %User{ap_id: recipient} <- User.get_cached_by_id(id),
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
conn conn
|> put_view(ChatView) |> put_view(ChatView)
@ -174,8 +162,8 @@ def create(%{assigns: %{user: user}} = conn, params) do
end end
end end
def show(%{assigns: %{user: user}} = conn, params) do def show(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
conn conn
|> put_view(ChatView) |> put_view(ChatView)
|> render("show.json", chat: chat) |> render("show.json", chat: chat)

View file

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

View file

@ -14,10 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
:download, :download,
:create, :create,
:update, :update,
:delete, :delete
:add_file,
:update_file,
:delete_file
] ]
) )
@ -184,105 +181,6 @@ def update(%{body_params: %{metadata: metadata}} = conn, %{name: name}) do
end end
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 def import_from_filesystem(conn, _params) do
with {:ok, names} <- Pack.import_from_filesystem() do with {:ok, names} <- Pack.import_from_filesystem() do
json(conn, names) json(conn, names)
@ -298,7 +196,4 @@ def import_from_filesystem(conn, _params) do
|> json(%{error: "Error accessing emoji pack directory"}) |> json(%{error: "Error accessing emoji pack directory"})
end end
end end
defp get_filename(%Plug.Upload{filename: filename}), do: filename
defp get_filename(url) when is_binary(url), do: Path.basename(url)
end end

View file

@ -238,9 +238,9 @@ defmodule Pleroma.Web.Router do
patch("/:name", EmojiPackController, :update) patch("/:name", EmojiPackController, :update)
delete("/:name", EmojiPackController, :delete) delete("/:name", EmojiPackController, :delete)
post("/:name/files", EmojiPackController, :add_file) post("/:name/files", EmojiFileController, :create)
patch("/:name/files", EmojiPackController, :update_file) patch("/:name/files", EmojiFileController, :update)
delete("/:name/files", EmojiPackController, :delete_file) delete("/:name/files", EmojiFileController, :delete)
end end
# Pack info / downloading # Pack info / downloading

View file

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

93
test/emoji/pack_test.exs Normal file
View file

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

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EmojiTest do defmodule Pleroma.EmojiTest do
use ExUnit.Case, async: true use ExUnit.Case
alias Pleroma.Emoji alias Pleroma.Emoji
describe "is_unicode_emoji?/1" do describe "is_unicode_emoji?/1" do

BIN
test/fixtures/emojis.zip vendored Normal file

Binary file not shown.

BIN
test/fixtures/empty.zip vendored Normal file

Binary file not shown.

37
test/user/query_test.exs Normal file
View file

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

View file

@ -17,6 +17,40 @@ defmodule Pleroma.UserSearchTest do
describe "User.search" do describe "User.search" do
setup do: clear_config([:instance, :limit_to_local_content]) 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 test "excludes invisible users from results" do
user = insert(:user, %{nickname: "john t1000"}) user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{invisible: true, nickname: "john t800"}) insert(:user, %{invisible: true, nickname: "john t800"})

View file

@ -509,7 +509,12 @@ test "it sends a confirm email" do
cng = User.register_changeset(%User{}, @full_user_data) cng = User.register_changeset(%User{}, @full_user_data)
{:ok, registered_user} = User.register(cng) {:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all() 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 end
test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do

15
test/utils_test.exs Normal file
View file

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

View file

@ -157,12 +157,12 @@ test "Mastodon Question activity with custom emojis" do
} }
end 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!() data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!()
assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) 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 end
test "accepts a Question with no content" do test "accepts a Question with no content" do

View file

@ -1977,7 +1977,12 @@ test "it resend emails for two users", %{conn: conn, admin: admin} do
}" }"
ObanHelpers.perform_all() 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
end end

View file

@ -29,6 +29,23 @@ defmodule Pleroma.Web.CommonAPITest do
setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :limit])
setup do: clear_config([:instance, :max_pinned_statuses]) 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 describe "blocking" do
setup do setup do
blocker = insert(:user) blocker = insert(:user)

View file

@ -61,7 +61,7 @@ test "redirects to the getting-started page when referer is not present", %{conn
end end
test "it returns 204", %{conn: conn} do test "it returns 204", %{conn: conn} do
assert json_response(conn, :no_content) assert empty_json_response(conn)
end end
test "it creates a PasswordResetToken record for user", %{user: user} do test "it creates a PasswordResetToken record for user", %{user: user} do
@ -91,7 +91,7 @@ test "it returns 204", %{conn: conn} do
assert conn assert conn
|> post("/auth/password?nickname=#{user.nickname}") |> post("/auth/password?nickname=#{user.nickname}")
|> json_response(:no_content) |> empty_json_response()
ObanHelpers.perform_all() ObanHelpers.perform_all()
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
@ -112,7 +112,7 @@ test "it doesn't fail when a user has no email", %{conn: conn} do
assert conn assert conn
|> post("/auth/password?nickname=#{user.nickname}") |> post("/auth/password?nickname=#{user.nickname}")
|> json_response(:no_content) |> empty_json_response()
end end
end end
@ -125,24 +125,21 @@ test "it doesn't fail when a user has no email", %{conn: conn} do
test "it returns 204 when user is not found", %{conn: conn, user: user} do test "it returns 204 when user is not found", %{conn: conn, user: user} do
conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
assert conn assert empty_json_response(conn)
|> json_response(:no_content)
end end
test "it returns 204 when user is not local", %{conn: conn, user: user} do test "it returns 204 when user is not local", %{conn: conn, user: user} do
{:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false))
conn = post(conn, "/auth/password?email=#{user.email}") conn = post(conn, "/auth/password?email=#{user.email}")
assert conn assert empty_json_response(conn)
|> json_response(:no_content)
end end
test "it returns 204 when user is deactivated", %{conn: conn, user: user} do 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)) {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true))
conn = post(conn, "/auth/password?email=#{user.email}") conn = post(conn, "/auth/password?email=#{user.email}")
assert conn assert empty_json_response(conn)
|> json_response(:no_content)
end end
end end

View file

@ -2,7 +2,7 @@
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase
alias Pleroma.Chat alias Pleroma.Chat
alias Pleroma.Chat.MessageReference alias Pleroma.Chat.MessageReference
@ -201,17 +201,39 @@ test "it paginates", %{conn: conn, user: user} do
chat = Chat.get(user.id, recipient.ap_id) chat = Chat.get(user.id, recipient.ap_id)
result = response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
conn result = json_response_and_validate_schema(response, 200)
|> get("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(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 assert length(result) == 20
result = response =
conn get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
|> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
|> json_response_and_validate_schema(200) 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 assert length(result) == 10
end end
@ -240,12 +262,10 @@ test "it returns the messages for a given chat", %{conn: conn, user: user} do
assert length(result) == 3 assert length(result) == 3
# Trying to get the chat of a different user # Trying to get the chat of a different user
result =
conn conn
|> assign(:user, other_user) |> assign(:user, other_user)
|> get("/api/v1/pleroma/chats/#{chat.id}/messages") |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(404)
assert result |> json_response(404)
end end
end end

View file

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

View file

@ -411,293 +411,6 @@ test "when the fallback source doesn't have all the files", ctx do
end end
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 describe "POST/DELETE /api/pleroma/emoji/packs/:name" do
test "creating and deleting a pack", %{admin_conn: admin_conn} do test "creating and deleting a pack", %{admin_conn: admin_conn} do
assert admin_conn assert admin_conn