Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms

This commit is contained in:
lain 2020-05-29 12:36:17 +02:00
commit af6d01ec93
65 changed files with 449 additions and 157 deletions

View File

@ -274,7 +274,7 @@ config :pleroma, :markup,
config :pleroma, :frontend_configurations, config :pleroma, :frontend_configurations,
pleroma_fe: %{ pleroma_fe: %{
alwaysShowSubjectInput: true, alwaysShowSubjectInput: true,
background: "/static/aurora_borealis.jpg", background: "/images/city.jpg",
collapseMessageWithSubject: false, collapseMessageWithSubject: false,
disableChat: false, disableChat: false,
greentext: false, greentext: false,

View File

@ -511,7 +511,23 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `discoverable` - `discoverable`
- `actor_type` - `actor_type`
- Response: none (code `200`) - Response:
```json
{"status": "success"}
```
```json
{"errors":
{"actor_type": "is invalid"},
{"email": "has invalid format"},
...
}
```
```json
{"error": "Unable to update user."}
```
## `GET /api/pleroma/admin/reports` ## `GET /api/pleroma/admin/reports`

View File

@ -0,0 +1,31 @@
# Optimizing your PostgreSQL performance
Pleroma performance depends to a large extent on good database performance. The default PostgreSQL settings are mostly fine, but often you can get better performance by changing a few settings.
You can use [PGTune](https://pgtune.leopard.in.ua) to get recommendations for your setup. If you do, set the "Number of Connections" field to 20, as Pleroma will only use 10 concurrent connections anyway. If you don't, it will give you advice that might even hurt your performance.
We also recommend not using the "Network Storage" option.
## Example configurations
Here are some configuration suggestions for PostgreSQL 10+.
### 1GB RAM, 1 CPU
```
shared_buffers = 256MB
effective_cache_size = 768MB
maintenance_work_mem = 64MB
work_mem = 13107kB
```
### 2GB RAM, 2 CPU
```
shared_buffers = 512MB
effective_cache_size = 1536MB
maintenance_work_mem = 128MB
work_mem = 26214kB
max_worker_processes = 2
max_parallel_workers_per_gather = 1
max_parallel_workers = 2
```

View File

@ -38,8 +38,8 @@ sudo apt install git build-essential postgresql postgresql-contrib
* Download and add the Erlang repository: * Download and add the Erlang repository:
```shell ```shell
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
``` ```
* Install Elixir and Erlang: * Install Elixir and Erlang:

View File

@ -40,8 +40,8 @@ sudo apt install git build-essential postgresql postgresql-contrib
* Erlangのリポジトリをダウンロードおよびインストールします。 * Erlangのリポジトリをダウンロードおよびインストールします。
``` ```
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
``` ```
* ElixirとErlangをインストールします、 * ElixirとErlangをインストールします、

View File

@ -63,7 +63,7 @@ apt install postgresql-11-rum
``` ```
#### (Optional) Performance configuration #### (Optional) Performance configuration
For optimal performance, you may use [PGTune](https://pgtune.leopard.in.ua), don't forget to restart postgresql after editing the configuration It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning.
```sh tab="Alpine" ```sh tab="Alpine"
rc-service postgresql restart rc-service postgresql restart

View File

@ -15,7 +15,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
{options, [], []} = parse_global_opts(args) {options, [], []} = parse_global_opts(args)
url_or_path = options[:manifest] || default_manifest() url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path) manifest = fetch_and_decode(url_or_path)
Enum.each(manifest, fn {name, info} -> Enum.each(manifest, fn {name, info} ->
to_print = [ to_print = [
@ -42,12 +42,12 @@ defmodule Mix.Tasks.Pleroma.Emoji do
url_or_path = options[:manifest] || default_manifest() url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path) manifest = fetch_and_decode(url_or_path)
for pack_name <- pack_names do for pack_name <- pack_names do
if Map.has_key?(manifest, pack_name) do if Map.has_key?(manifest, pack_name) do
pack = manifest[pack_name] pack = manifest[pack_name]
src_url = pack["src"] src = pack["src"]
IO.puts( IO.puts(
IO.ANSI.format([ IO.ANSI.format([
@ -57,11 +57,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do
:normal, :normal,
" from ", " from ",
:underline, :underline,
src_url src
]) ])
) )
binary_archive = Tesla.get!(client(), src_url).body {:ok, binary_archive} = fetch(src)
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16() archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright] sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
@ -74,8 +74,8 @@ defmodule Mix.Tasks.Pleroma.Emoji do
raise "Bad SHA256 for #{pack_name}" raise "Bad SHA256 for #{pack_name}"
end end
# The url specified in files should be in the same directory # The location specified in files should be in the same directory
files_url = files_loc =
url_or_path url_or_path
|> Path.dirname() |> Path.dirname()
|> Path.join(pack["files"]) |> Path.join(pack["files"])
@ -88,11 +88,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do
:normal, :normal,
" from ", " from ",
:underline, :underline,
files_url files_loc
]) ])
) )
files = Tesla.get!(client(), files_url).body |> Jason.decode!() files = fetch_and_decode(files_loc)
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name])) IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
@ -237,16 +237,20 @@ defmodule Mix.Tasks.Pleroma.Emoji do
end end
end end
defp fetch_manifest(from) do defp fetch_and_decode(from) do
Jason.decode!( with {:ok, json} <- fetch(from) do
if String.starts_with?(from, "http") do Jason.decode!(json)
Tesla.get!(client(), from).body end
else
File.read!(from)
end
)
end end
defp fetch("http" <> _ = from) do
with {:ok, %{body: body}} <- Tesla.get(client(), from) do
{:ok, body}
end
end
defp fetch(path), do: File.read(path)
defp parse_global_opts(args) do defp parse_global_opts(args) do
OptionParser.parse( OptionParser.parse(
args, args,

View File

@ -24,10 +24,7 @@ defmodule Pleroma.Activity.Queries do
@spec by_actor(query, String.t()) :: query @spec by_actor(query, String.t()) :: query
def by_actor(query \\ Activity, actor) do def by_actor(query \\ Activity, actor) do
from( from(a in query, where: a.actor == ^actor)
activity in query,
where: fragment("(?)->>'actor' = ?", activity.data, ^actor)
)
end end
@spec by_author(query, User.t()) :: query @spec by_author(query, User.t()) :: query

View File

@ -63,7 +63,7 @@ defmodule Pleroma.Conversation do
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
{:ok, conversation} = create_for_ap_id(ap_id) {:ok, conversation} = create_for_ap_id(ap_id)
users = User.get_users_from_set(activity.recipients, false) users = User.get_users_from_set(activity.recipients, local_only: false)
participations = participations =
Enum.map(users, fn user -> Enum.map(users, fn user ->

View File

@ -499,7 +499,7 @@ defmodule Pleroma.Emoji.Pack do
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
{:ok, archive} {:ok, archive}
else else
{:error, :imvalid_checksum} {:error, :invalid_checksum}
end end
end end
end end

View File

@ -92,8 +92,9 @@ defmodule Pleroma.Notification do
|> join(:left, [n, a], object in Object, |> join(:left, [n, a], object in Object,
on: on:
fragment( fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
object.data, object.data,
a.data,
a.data a.data
) )
) )
@ -224,18 +225,8 @@ defmodule Pleroma.Notification do
|> Marker.multi_set_last_read_id(user, "notifications") |> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction() |> Repo.transaction()
Notification for_user_query(user)
|> where([n], n.id in ^notification_ids) |> where([n], n.id in ^notification_ids)
|> join(:inner, [n], activity in assoc(n, :activity))
|> join(:left, [n, a], object in Object,
on:
fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
object.data,
a.data
)
)
|> preload([n, a, o], activity: {a, object: o})
|> Repo.all() |> Repo.all()
end end
@ -370,7 +361,8 @@ defmodule Pleroma.Notification do
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
potential_receivers = User.get_users_from_set(potential_receiver_ap_ids, local_only) potential_receivers =
User.get_users_from_set(potential_receiver_ap_ids, local_only: local_only)
notification_enabled_ap_ids = notification_enabled_ap_ids =
potential_receiver_ap_ids potential_receiver_ap_ids

View File

@ -31,7 +31,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
{"x-content-type-options", "nosniff"}, {"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy}, {"referrer-policy", referrer_policy},
{"x-download-options", "noopen"}, {"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"} {"content-security-policy", csp_string()}
] ]
if report_uri do if report_uri do
@ -43,23 +43,46 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
] ]
} }
headers ++ [{"reply-to", Jason.encode!(report_group)}] [{"reply-to", Jason.encode!(report_group)} | headers]
else else
headers headers
end end
end end
static_csp_rules = [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'"
]
@csp_start [Enum.join(static_csp_rules, ";") <> ";"]
defp csp_string do defp csp_string do
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
static_url = Pleroma.Web.Endpoint.static_url() static_url = Pleroma.Web.Endpoint.static_url()
websocket_url = Pleroma.Web.Endpoint.websocket_url() websocket_url = Pleroma.Web.Endpoint.websocket_url()
report_uri = Config.get([:http_security, :report_uri]) report_uri = Config.get([:http_security, :report_uri])
connect_src = "connect-src 'self' #{static_url} #{websocket_url}" img_src = "img-src 'self' data: blob:"
media_src = "media-src 'self'"
{img_src, media_src} =
if Config.get([:media_proxy, :enabled]) &&
!Config.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do
sources = get_proxy_and_attachment_sources()
{[img_src, sources], [media_src, sources]}
else
{img_src, media_src}
end
connect_src = ["connect-src 'self' ", static_url, ?\s, websocket_url]
connect_src = connect_src =
if Pleroma.Config.get(:env) == :dev do if Pleroma.Config.get(:env) == :dev do
connect_src <> " http://localhost:3035/" [connect_src, " http://localhost:3035/"]
else else
connect_src connect_src
end end
@ -71,27 +94,46 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
"script-src 'self'" "script-src 'self'"
end end
main_part = [ report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
"default-src 'none'", insecure = if scheme == "https", do: "upgrade-insecure-requests"
"base-uri 'self'",
"frame-ancestors 'none'",
"img-src 'self' data: blob: https:",
"media-src 'self' https:",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'",
connect_src,
script_src
]
report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: [] @csp_start
|> add_csp_param(img_src)
insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: [] |> add_csp_param(media_src)
|> add_csp_param(connect_src)
(main_part ++ report ++ insecure) |> add_csp_param(script_src)
|> Enum.join("; ") |> add_csp_param(insecure)
|> add_csp_param(report)
|> :erlang.iolist_to_binary()
end end
defp get_proxy_and_attachment_sources do
media_proxy_whitelist =
Enum.reduce(Config.get([:media_proxy, :whitelist]), [], fn host, acc ->
add_source(acc, host)
end)
upload_base_url =
if Config.get([Pleroma.Upload, :base_url]),
do: URI.parse(Config.get([Pleroma.Upload, :base_url])).host
s3_endpoint =
if Config.get([Pleroma.Upload, :uploader]) == Pleroma.Uploaders.S3,
do: URI.parse(Config.get([Pleroma.Uploaders.S3, :public_endpoint])).host
[]
|> add_source(upload_base_url)
|> add_source(s3_endpoint)
|> add_source(media_proxy_whitelist)
end
defp add_source(iodata, nil), do: iodata
defp add_source(iodata, source), do: [[?\s, source] | iodata]
defp add_csp_param(csp_iodata, nil), do: csp_iodata
defp add_csp_param(csp_iodata, param), do: [[param, ?;] | csp_iodata]
def warn_if_disabled do def warn_if_disabled do
unless Config.get([:http_security, :enabled]) do unless Config.get([:http_security, :enabled]) do
Logger.warn(" Logger.warn("

View File

@ -538,9 +538,10 @@ defmodule Pleroma.User do
|> delete_change(:also_known_as) |> delete_change(:also_known_as)
|> unique_constraint(:email) |> unique_constraint(:email)
|> validate_format(:email, @email_regex) |> validate_format(:email, @email_regex)
|> validate_inclusion(:actor_type, ["Person", "Service"])
end end
@spec update_as_admin(%User{}, map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} @spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def update_as_admin(user, params) do def update_as_admin(user, params) do
params = Map.put(params, "password_confirmation", params["password"]) params = Map.put(params, "password_confirmation", params["password"])
changeset = update_as_admin_changeset(user, params) changeset = update_as_admin_changeset(user, params)
@ -561,7 +562,7 @@ defmodule Pleroma.User do
|> put_change(:password_reset_pending, false) |> put_change(:password_reset_pending, false)
end end
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} @spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def reset_password(%User{} = user, params) do def reset_password(%User{} = user, params) do
reset_password(user, user, params) reset_password(user, user, params)
end end
@ -1208,8 +1209,9 @@ defmodule Pleroma.User do
def increment_unread_conversation_count(_, user), do: {:ok, user} def increment_unread_conversation_count(_, user), do: {:ok, user}
@spec get_users_from_set([String.t()], boolean()) :: [User.t()] @spec get_users_from_set([String.t()], keyword()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do def get_users_from_set(ap_ids, opts \\ []) do
local_only = Keyword.get(opts, :local_only, true)
criteria = %{ap_id: ap_ids, deactivated: false} criteria = %{ap_id: ap_ids, deactivated: false}
criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
@ -1618,12 +1620,19 @@ defmodule Pleroma.User do
def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id) def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
def get_or_fetch_by_ap_id(ap_id) do def get_or_fetch_by_ap_id(ap_id) do
user = get_cached_by_ap_id(ap_id) cached_user = get_cached_by_ap_id(ap_id)
if !is_nil(user) and !needs_update?(user) do maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id)
{:ok, user}
else case {cached_user, maybe_fetched_user} do
fetch_by_ap_id(ap_id) {_, {:ok, %User{} = user}} ->
{:ok, user}
{%User{} = user, _} ->
{:ok, user}
_ ->
{:error, :not_found}
end end
end end

View File

@ -545,14 +545,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Repo.one() |> Repo.one()
end end
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()] @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
opts = Map.drop(opts, ["user"]) opts = Map.drop(opts, ["user"])
[Constants.as_public()] query = fetch_activities_query([Constants.as_public()], opts)
|> fetch_activities_query(opts)
|> restrict_unlisted() query =
|> Pagination.fetch_paginated(opts, pagination) if opts["restrict_unlisted"] do
restrict_unlisted(query)
else
query
end
Pagination.fetch_paginated(query, opts, pagination)
end
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
opts
|> Map.put("restrict_unlisted", true)
|> fetch_public_or_unlisted_activities(pagination)
end end
@valid_visibilities ~w[direct unlisted public private] @valid_visibilities ~w[direct unlisted public private]
@ -1165,7 +1178,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Activity.with_joined_object() |> Activity.with_joined_object()
|> Object.with_joined_activity() |> Object.with_joined_activity()
|> select([_like, object, activity], %{activity | object: object}) |> select([_like, object, activity], %{activity | object: object})
|> order_by([like, _, _], desc: like.id) |> order_by([like, _, _], desc_nulls_last: like.id)
|> Pagination.fetch_paginated( |> Pagination.fetch_paginated(
Map.merge(params, %{"skip_order" => true}), Map.merge(params, %{"skip_order" => true}),
pagination, pagination,

View File

@ -1055,10 +1055,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Map.put(object, "tag", tags) Map.put(object, "tag", tags)
end end
# TODO These should be added on our side on insertion, it doesn't make much
# sense to regenerate these all the time
def add_mention_tags(object) do def add_mention_tags(object) do
{enabled_receivers, disabled_receivers} = Utils.get_notified_from_object(object) to = object["to"] || []
potential_receivers = enabled_receivers ++ disabled_receivers cc = object["cc"] || []
mentions = Enum.map(potential_receivers, &build_mention_tag/1) mentioned = User.get_users_from_set(to ++ cc, local_only: false)
mentions = Enum.map(mentioned, &build_mention_tag/1)
tags = object["tag"] || [] tags = object["tag"] || []
Map.put(object, "tag", tags ++ mentions) Map.put(object, "tag", tags ++ mentions)

View File

@ -693,7 +693,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
%{assigns: %{user: admin}} = conn, %{assigns: %{user: admin}} = conn,
%{"nickname" => nickname} = params %{"nickname" => nickname} = params
) do ) do
with {_, user} <- {:user, User.get_cached_by_nickname(nickname)}, with {_, %User{} = user} <- {:user, User.get_cached_by_nickname(nickname)},
{:ok, _user} <- {:ok, _user} <-
User.update_as_admin(user, params) do User.update_as_admin(user, params) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
@ -715,11 +715,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
json(conn, %{status: "success"}) json(conn, %{status: "success"})
else else
{:error, changeset} -> {:error, changeset} ->
{_, {error, _}} = Enum.at(changeset.errors, 0) errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
json(conn, %{error: "New password #{error}."})
json(conn, %{errors: errors})
_ -> _ ->
json(conn, %{error: "Unable to change password."}) json(conn, %{error: "Unable to update user."})
end end
end end

View File

@ -56,7 +56,7 @@ defmodule Pleroma.Web.Feed.UserController do
"actor_id" => user.ap_id "actor_id" => user.ap_id
} }
|> put_if_exist("max_id", params["max_id"]) |> put_if_exist("max_id", params["max_id"])
|> ActivityPub.fetch_public_activities() |> ActivityPub.fetch_public_or_unlisted_activities()
conn conn
|> put_resp_content_type("application/#{format}+xml") |> put_resp_content_type("application/#{format}+xml")

View File

@ -81,7 +81,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug( plug(
RateLimiter, RateLimiter,
[name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions [name: :relation_id_action, params: [:id, :uri]] when action in @relationship_actions
) )
plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions) plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions)

View File

@ -84,13 +84,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
plug( plug(
RateLimiter, RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]] [name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: [:id]]
when action in ~w(reblog unreblog)a when action in ~w(reblog unreblog)a
) )
plug( plug(
RateLimiter, RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]] [name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: [:id]]
when action in ~w(favourite unfavourite)a when action in ~w(favourite unfavourite)a
) )

View File

@ -182,12 +182,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
bot = user.actor_type in ["Application", "Service"] bot = user.actor_type in ["Application", "Service"]
emojis = emojis =
Enum.map(user.emoji, fn {shortcode, url} -> Enum.map(user.emoji, fn {shortcode, raw_url} ->
url = MediaProxy.url(raw_url)
%{ %{
"shortcode" => shortcode, shortcode: shortcode,
"url" => url, url: url,
"static_url" => url, static_url: url,
"visible_in_picker" => false visible_in_picker: false
} }
end) end)

View File

@ -106,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> json(%{error: "The requested instance does not support sharing emoji packs"}) |> json(%{error: "The requested instance does not support sharing emoji packs"})
{:error, :imvalid_checksum} -> {:error, :invalid_checksum} ->
conn conn
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})

View File

@ -16,6 +16,8 @@ defmodule Pleroma.Workers.Cron.ClearOauthTokenWorker do
def perform(_opts, _job) do def perform(_opts, _job) do
if Config.get([:oauth2, :clean_expired_tokens], false) do if Config.get([:oauth2, :clean_expired_tokens], false) do
Token.delete_expired_tokens() Token.delete_expired_tokens()
else
:ok
end end
end end
end end

View File

@ -37,6 +37,8 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
) )
|> Repo.all() |> Repo.all()
|> send_emails |> send_emails
else
:ok
end end
end end

View File

@ -55,7 +55,11 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
|> Repo.all() |> Repo.all()
|> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses)) |> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses))
|> Enum.each(&Pleroma.Emails.Mailer.deliver/1) |> Enum.each(&Pleroma.Emails.Mailer.deliver/1)
else
:ok
end end
else
:ok
end end
end end
end end

View File

@ -23,6 +23,8 @@ defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do
def perform(_opts, _job) do def perform(_opts, _job) do
if Config.get([ActivityExpiration, :enabled]) do if Config.get([ActivityExpiration, :enabled]) do
Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1) Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1)
else
:ok
end end
end end

View File

@ -0,0 +1,7 @@
defmodule Pleroma.Repo.Migrations.AddAppsIndexes do
use Ecto.Migration
def change do
create(index(:apps, [:client_id, :client_secret]))
end
end

View File

@ -0,0 +1,8 @@
defmodule Pleroma.Repo.Migrations.ChangeNotificationUserIndex do
use Ecto.Migration
def change do
drop_if_exists(index(:notifications, [:user_id]))
create_if_not_exists(index(:notifications, [:user_id, "id desc nulls last"]))
end
end

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.status-card{margin-bottom:10px}.status-card .account{text-decoration:underline;line-height:26px;font-size:13px}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;height:22px}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.statuses-header-container .el-button.is-plain:focus,.statuses-header-container .el-button.is-plain:hover{border-color:#dcdfe6;color:#606266;cursor:default}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button-group{width:100%}.statuses-header-container .el-button{padding:10px 6.5px;width:50%}.statuses-header-container .el-button-group>.el-button:first-child{border-bottom-left-radius:0}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).private-button{border-top-right-radius:4px}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).public-button{border-bottom-left-radius:4px;border-top:#fff}.statuses-header-container .el-button-group>.el-button:last-child{border-top-right-radius:0;border-top:#fff}.statuses-header-container .reboot-button{margin:10px 0 0}}

View File

@ -1 +0,0 @@
.status-card{margin-bottom:10px}.status-card .account{text-decoration:underline;line-height:26px;font-size:13px}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;height:22px}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button{padding:10px 6.5px}.statuses-header-container .reboot-button{margin:10px 0 0}}

View File

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.796ca6d4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.1b4f6ce0.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.203f69f8.js></script></body></html> <!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.796ca6d4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.b08eb412.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.0146039c.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
!function(e){function n(n){for(var r,c,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)c=a[i],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-6b68":1,"chunk-d38a":1,"chunk-0558":1,"chunk-0778":1,"chunk-3384":1,"chunk-6e81":1,"chunk-4011":1,"chunk-970d":1,"chunk-22d2":1,"chunk-e458":1,"chunk-7637":1,"chunk-0961":1}[e]&&n.push(c[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0",ZhIB:"31d6cfe0","chunk-6b68":"0cc00484","chunk-d38a":"cabdc22e","chunk-0558":"af0d89cd","chunk-0778":"d9e7180a","chunk-3384":"2278f87c","chunk-6e81":"0e80d020","chunk-7f9e":"31d6cfe0","chunk-4011":"c4799067","chunk-df62":"31d6cfe0","chunk-970d":"f59cca8c","chunk-22d2":"813009b9","chunk-e458":"f88bafea","chunk-7637":"941c4edb",oAJy:"31d6cfe0","chunk-0961":"d3692214","chunk-16d0":"31d6cfe0"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===r||f===c))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===r||f===c)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},d.href=c,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){c[e]=0}));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=u[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594",ZhIB:"861df339","chunk-6b68":"fbc0f684","chunk-d38a":"a851004a","chunk-0558":"75954137","chunk-0778":"b17650df","chunk-3384":"458ffaf1","chunk-6e81":"3733ace2","chunk-7f9e":"c49aa694","chunk-4011":"67fb1692","chunk-df62":"6c5105a6","chunk-970d":"2457e066","chunk-22d2":"a0cf7976","chunk-e458":"4e5aad44","chunk-7637":"8f5fb36e",oAJy:"840fb1c2","chunk-0961":"ef33e81b","chunk-16d0":"6ce78978"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;t()}([]); !function(e){function n(n){for(var r,c,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)c=a[i],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-6b68":1,"chunk-d38a":1,"chunk-0558":1,"chunk-0778":1,"chunk-3384":1,"chunk-6e81":1,"chunk-7e30":1,"chunk-e458":1,"chunk-970d":1,"chunk-22d2":1,"chunk-7637":1,"chunk-0961":1}[e]&&n.push(c[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0",ZhIB:"31d6cfe0","chunk-6b68":"0cc00484","chunk-d38a":"cabdc22e","chunk-0558":"af0d89cd","chunk-0778":"d9e7180a","chunk-3384":"d50ed383","chunk-6e81":"0e80d020","chunk-7f9e":"31d6cfe0","chunk-7e30":"f2b9674a","chunk-df62":"31d6cfe0","chunk-e458":"6c0703cb","chunk-970d":"f59cca8c","chunk-22d2":"813009b9","chunk-7637":"941c4edb",oAJy:"31d6cfe0","chunk-0961":"d3692214","chunk-16d0":"31d6cfe0"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===r||f===c))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===r||f===c)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},d.href=c,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){c[e]=0}));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=u[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594",ZhIB:"861df339","chunk-6b68":"fbc0f684","chunk-d38a":"a851004a","chunk-0558":"75954137","chunk-0778":"b17650df","chunk-3384":"b2ebeeca","chunk-6e81":"3733ace2","chunk-7f9e":"c49aa694","chunk-7e30":"ec42e302","chunk-df62":"6c5105a6","chunk-e458":"bb460d81","chunk-970d":"2457e066","chunk-22d2":"a0cf7976","chunk-7637":"8f5fb36e",oAJy:"840fb1c2","chunk-0961":"ef33e81b","chunk-16d0":"6ce78978"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;t()}([]);
//# sourceMappingURL=runtime.1b4f6ce0.js.map //# sourceMappingURL=runtime.b08eb412.js.map

View File

@ -0,0 +1,3 @@
{
"blank": "blank.png"
}

View File

@ -0,0 +1,10 @@
{
"local": {
"src_sha256": "384025A1AC6314473863A11AC7AB38A12C01B851A3F82359B89B4D4211D3291D",
"src": "test/fixtures/emoji/packs/blank.png.zip",
"license": "Apache 2.0",
"homepage": "https://example.com",
"files": "files.json",
"description": "Some local pack"
}
}

View File

@ -454,8 +454,7 @@ defmodule Pleroma.NotificationTest do
status: "hey again @#{other_user.nickname}!" status: "hey again @#{other_user.nickname}!"
}) })
[n2, n1] = notifs = Notification.for_user(other_user) [n2, n1] = Notification.for_user(other_user)
assert length(notifs) == 2
assert n2.id > n1.id assert n2.id > n1.id
@ -464,7 +463,9 @@ defmodule Pleroma.NotificationTest do
status: "hey yet again @#{other_user.nickname}!" status: "hey yet again @#{other_user.nickname}!"
}) })
Notification.set_read_up_to(other_user, n2.id) [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
assert read_notification.activity.object
[n3, n2, n1] = Notification.for_user(other_user) [n3, n2, n1] = Notification.for_user(other_user)
@ -972,7 +973,9 @@ defmodule Pleroma.NotificationTest do
{:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
assert length(Notification.for_user(user)) == 1 [notification] = Notification.for_user(user)
assert notification.activity.object
end end
test "it doesn't return notifications for muted user with notifications" do test "it doesn't return notifications for muted user with notifications" do

View File

@ -68,6 +68,7 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
assert "$pbkdf2" <> _ = user.password_hash assert "$pbkdf2" <> _ = user.password_hash
end end
@tag :skip_on_mac
test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
user = user =
insert(:user, insert(:user,

View File

@ -67,7 +67,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
[csp] = Conn.get_resp_header(conn, "content-security-policy") [csp] = Conn.get_resp_header(conn, "content-security-policy")
assert csp =~ ~r|report-uri https://endpoint.com; report-to csp-endpoint;| assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;|
[reply_to] = Conn.get_resp_header(conn, "reply-to") [reply_to] = Conn.get_resp_header(conn, "reply-to")

View File

@ -34,7 +34,8 @@ defmodule Pleroma.Factory do
last_digest_emailed_at: NaiveDateTime.utc_now(), last_digest_emailed_at: NaiveDateTime.utc_now(),
last_refreshed_at: NaiveDateTime.utc_now(), last_refreshed_at: NaiveDateTime.utc_now(),
notification_settings: %Pleroma.User.NotificationSetting{}, notification_settings: %Pleroma.User.NotificationSetting{},
multi_factor_authentication_settings: %Pleroma.MFA.Settings{} multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
ap_enabled: true
} }
%{ %{

View File

@ -73,6 +73,19 @@ defmodule Mix.Tasks.Pleroma.EmojiTest do
on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end) on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end)
end end
test "install local emoji pack" do
assert capture_io(fn ->
Emoji.run([
"get-packs",
"local",
"--manifest",
"test/instance_static/local_pack/manifest.json"
])
end) =~ "Writing pack.json for"
on_exit(fn -> File.rm_rf!("test/instance_static/emoji/local") end)
end
test "pack not found" do test "pack not found" do
mock(fn mock(fn
%{ %{

View File

@ -586,6 +586,26 @@ defmodule Pleroma.UserTest do
refute user.last_refreshed_at == orig_user.last_refreshed_at refute user.last_refreshed_at == orig_user.last_refreshed_at
end end
@tag capture_log: true
test "it returns the old user if stale, but unfetchable" do
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
orig_user =
insert(
:user,
local: false,
nickname: "admin@mastodon.example.org",
ap_id: "http://mastodon.example.org/users/raymoo",
last_refreshed_at: a_week_ago
)
assert orig_user.last_refreshed_at == a_week_ago
{:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo")
assert user.last_refreshed_at == orig_user.last_refreshed_at
end
end end
test "returns an ap_id for a user" do test "returns an ap_id for a user" do

View File

@ -451,6 +451,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert Activity.get_by_ap_id(data["id"]) assert Activity.get_by_ap_id(data["id"])
end end
@tag capture_log: true
test "it inserts an incoming activity into the database" <>
"even if we can't fetch the user but have it in our db",
%{conn: conn} do
user =
insert(:user,
ap_id: "https://mastodon.example.org/users/raymoo",
ap_enabled: true,
local: false,
last_refreshed_at: nil
)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("actor", user.ap_id)
|> put_in(["object", "attridbutedTo"], user.ap_id)
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)
assert "ok" == json_response(conn, 200)
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
end
test "it clears `unreachable` federation status of the sender", %{conn: conn} do test "it clears `unreachable` federation status of the sender", %{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()

View File

@ -1094,23 +1094,28 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
{:ok, activity} = {:ok, activity} =
CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) with_mock Pleroma.Notification,
object = modified["object"] get_notified_from_activity: fn _, _ -> [] end do
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
expected_mention = %{ object = modified["object"]
"href" => other_user.ap_id,
"name" => "@#{other_user.nickname}",
"type" => "Mention"
}
expected_tag = %{ expected_mention = %{
"href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", "href" => other_user.ap_id,
"type" => "Hashtag", "name" => "@#{other_user.nickname}",
"name" => "#2hu" "type" => "Mention"
} }
assert Enum.member?(object["tag"], expected_tag) expected_tag = %{
assert Enum.member?(object["tag"], expected_mention) "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
"type" => "Hashtag",
"name" => "#2hu"
}
refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
assert Enum.member?(object["tag"], expected_tag)
assert Enum.member?(object["tag"], expected_mention)
end
end end
test "it adds the sensitive property" do test "it adds the sensitive property" do

View File

@ -3191,8 +3191,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end
describe "PATCH /users/:nickname/credentials" do describe "PATCH /users/:nickname/credentials" do
test "changes password and email", %{conn: conn, admin: admin} do setup do
user = insert(:user) user = insert(:user)
[user: user]
end
test "changes password and email", %{conn: conn, admin: admin, user: user} do
assert user.password_reset_pending == false assert user.password_reset_pending == false
conn = conn =
@ -3222,9 +3226,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} forced password reset for users: @#{user.nickname}" "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
end end
test "returns 403 if requested by a non-admin" do test "returns 403 if requested by a non-admin", %{user: user} do
user = insert(:user)
conn = conn =
build_conn() build_conn()
|> assign(:user, user) |> assign(:user, user)
@ -3236,6 +3238,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, :forbidden) assert json_response(conn, :forbidden)
end end
test "changes actor type from permitted list", %{conn: conn, user: user} do
assert user.actor_type == "Person"
assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
"actor_type" => "Service"
})
|> json_response(200) == %{"status" => "success"}
updated_user = User.get_by_id(user.id)
assert updated_user.actor_type == "Service"
assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
"actor_type" => "Application"
})
|> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
end
test "update non existing user", %{conn: conn} do
assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
"password" => "new_password"
})
|> json_response(200) == %{"error" => "Unable to update user."}
end
end end
describe "PATCH /users/:nickname/force_password_reset" do describe "PATCH /users/:nickname/force_password_reset" do

View File

@ -11,13 +11,14 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI
setup do: clear_config([:instance, :federating], true) setup do: clear_config([:instance, :federating], true)
describe "feed" do describe "feed" do
setup do: clear_config([:feed]) setup do: clear_config([:feed])
test "gets a feed", %{conn: conn} do test "gets an atom feed", %{conn: conn} do
Config.put( Config.put(
[:feed, :post_title], [:feed, :post_title],
%{max_length: 10, omission: "..."} %{max_length: 10, omission: "..."}
@ -157,6 +158,29 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
assert response(conn, 404) assert response(conn, 404)
end end
test "returns feed with public and unlisted activities", %{conn: conn} do
user = insert(:user)
{:ok, _} = CommonAPI.post(user, %{status: "public", visibility: "public"})
{:ok, _} = CommonAPI.post(user, %{status: "direct", visibility: "direct"})
{:ok, _} = CommonAPI.post(user, %{status: "unlisted", visibility: "unlisted"})
{:ok, _} = CommonAPI.post(user, %{status: "private", visibility: "private"})
resp =
conn
|> put_req_header("accept", "application/atom+xml")
|> get(user_feed_path(conn, :feed, user.nickname))
|> response(200)
activity_titles =
resp
|> SweetXml.parse()
|> SweetXml.xpath(~x"//entry/title/text()"l)
|> Enum.sort()
assert activity_titles == ['public', 'unlisted']
end
end end
# Note: see ActivityPubControllerTest for JSON format tests # Note: see ActivityPubControllerTest for JSON format tests

View File

@ -54,10 +54,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
header_static: "http://localhost:4001/images/banner.png", header_static: "http://localhost:4001/images/banner.png",
emojis: [ emojis: [
%{ %{
"static_url" => "/file.png", static_url: "/file.png",
"url" => "/file.png", url: "/file.png",
"shortcode" => "karjalanpiirakka", shortcode: "karjalanpiirakka",
"visible_in_picker" => false visible_in_picker: false
} }
], ],
fields: [], fields: [],
@ -493,4 +493,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
AccountView.render("show.json", %{user: user, for: user}) AccountView.render("show.json", %{user: user, for: user})
end end
end end
test "uses mediaproxy urls when it's enabled" do
clear_config([:media_proxy, :enabled], true)
user =
insert(:user,
avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]},
banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]},
emoji: %{"joker_smile" => "https://evil.website/society.png"}
)
AccountView.render("show.json", %{user: user})
|> Enum.all?(fn
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
String.starts_with?(url, Pleroma.Web.base_url())
{:emojis, emojis} ->
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
String.starts_with?(url, Pleroma.Web.base_url()) &&
String.starts_with?(static_url, Pleroma.Web.base_url())
end)
_ ->
true
end)
|> assert()
end
end end

View File

@ -124,15 +124,7 @@ defmodule Pleroma.Web.MediaProxyTest do
end end
test "uses the configured base_url" do test "uses the configured base_url" do
base_url = Pleroma.Config.get([:media_proxy, :base_url]) clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
if base_url do
on_exit(fn ->
Pleroma.Config.put([:media_proxy, :base_url], base_url)
end)
end
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
url = "https://pleroma.soykaf.com/static/logo.png" url = "https://pleroma.soykaf.com/static/logo.png"
encoded = url(url) encoded = url(url)
@ -213,8 +205,8 @@ defmodule Pleroma.Web.MediaProxyTest do
end end
test "does not change whitelisted urls" do test "does not change whitelisted urls" do
Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"]) clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"])
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social") clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
media_url = "https://mycdn.akamai.com" media_url = "https://mycdn.akamai.com"