From 371a4aed2ca9f6926e49f6791c8b4d14292d20e5 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sat, 13 Apr 2019 17:40:42 +0700
Subject: [PATCH 001/202] Add User.Info.email_notifications
---
lib/pleroma/user/info.ex | 27 +++++++++++++++++++
.../20190412052952_add_user_info_fields.exs | 20 ++++++++++++++
test/user_info_test.exs | 24 +++++++++++++++++
3 files changed, 71 insertions(+)
create mode 100644 priv/repo/migrations/20190412052952_add_user_info_fields.exs
create mode 100644 test/user_info_test.exs
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 5afa7988c..194dd5581 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -8,6 +8,8 @@ defmodule Pleroma.User.Info do
alias Pleroma.User.Info
+ @type t :: %__MODULE__{}
+
embedded_schema do
field(:banner, :map, default: %{})
field(:background, :map, default: %{})
@@ -40,6 +42,7 @@ defmodule Pleroma.User.Info do
field(:hide_follows, :boolean, default: false)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
+ field(:email_notifications, :map, default: %{"digest" => true})
field(:notification_settings, :map,
default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
@@ -75,6 +78,30 @@ def update_notification_settings(info, settings) do
|> validate_required([:notification_settings])
end
+ @doc """
+ Update email notifications in the given User.Info struct.
+
+ Examples:
+
+ iex> update_email_notifications(%Pleroma.User.Info{email_notifications: %{"digest" => false}}, %{"digest" => true})
+ %Pleroma.User.Info{email_notifications: %{"digest" => true}}
+
+ """
+ @spec update_email_notifications(t(), map()) :: Ecto.Changeset.t()
+ def update_email_notifications(info, settings) do
+ email_notifications =
+ info.email_notifications
+ |> Map.merge(settings)
+ |> Map.take(["digest"])
+
+ params = %{email_notifications: email_notifications}
+ fields = [:email_notifications]
+
+ info
+ |> cast(params, fields)
+ |> validate_required(fields)
+ end
+
def add_to_note_count(info, number) do
set_note_count(info, info.note_count + number)
end
diff --git a/priv/repo/migrations/20190412052952_add_user_info_fields.exs b/priv/repo/migrations/20190412052952_add_user_info_fields.exs
new file mode 100644
index 000000000..203d0fc3b
--- /dev/null
+++ b/priv/repo/migrations/20190412052952_add_user_info_fields.exs
@@ -0,0 +1,20 @@
+defmodule Pleroma.Repo.Migrations.AddEmailNotificationsToUserInfo do
+ use Ecto.Migration
+
+ def up do
+ execute("
+ UPDATE users
+ SET info = info || '{
+ \"email_notifications\": {
+ \"digest\": true
+ }
+ }'")
+ end
+
+ def down do
+ execute("
+ UPDATE users
+ SET info = info - 'email_notifications'
+ ")
+ end
+end
diff --git a/test/user_info_test.exs b/test/user_info_test.exs
new file mode 100644
index 000000000..2d795594e
--- /dev/null
+++ b/test/user_info_test.exs
@@ -0,0 +1,24 @@
+defmodule Pleroma.UserInfoTest do
+ alias Pleroma.Repo
+ alias Pleroma.User.Info
+
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ describe "update_email_notifications/2" do
+ setup do
+ user = insert(:user, %{info: %{email_notifications: %{"digest" => true}}})
+
+ {:ok, user: user}
+ end
+
+ test "Notifications are updated", %{user: user} do
+ true = user.info.email_notifications["digest"]
+ changeset = Info.update_email_notifications(user.info, %{"digest" => false})
+ assert changeset.valid?
+ {:ok, result} = Ecto.Changeset.apply_action(changeset, :insert)
+ assert result.email_notifications["digest"] == false
+ end
+ end
+end
From dc21181f6504b55afa68de63f170fcb0f1084a6b Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 14 Apr 2019 22:29:05 +0700
Subject: [PATCH 002/202] Update updated_at field on notification read
---
lib/pleroma/notification.ex | 5 ++++-
test/notification_test.exs | 23 +++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index b357d5399..29845b9da 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -58,7 +58,10 @@ def set_read_up_to(%{id: user_id} = _user, id) do
where: n.user_id == ^user_id,
where: n.id <= ^id,
update: [
- set: [seen: true]
+ set: [
+ seen: true,
+ updated_at: ^NaiveDateTime.utc_now()
+ ]
]
)
diff --git a/test/notification_test.exs b/test/notification_test.exs
index c3db77b6c..907b9e669 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -300,6 +300,29 @@ test "it sets all notifications as read up to a specified notification ID" do
assert n2.seen == true
assert n3.seen == false
end
+
+ test "Updates `updated_at` field" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+
+ Enum.each(0..10, fn i ->
+ {:ok, _activity} =
+ TwitterAPI.create_status(user1, %{
+ "status" => "#{i} hi @#{user2.nickname}"
+ })
+ end)
+
+ Process.sleep(1000)
+
+ [notification | _] = Notification.for_user(user2)
+
+ Notification.set_read_up_to(user2, notification.id)
+
+ Notification.for_user(user2)
+ |> Enum.each(fn notification ->
+ assert notification.updated_at > notification.inserted_at
+ end)
+ end
end
describe "notification target determination" do
From 2f0203a4a1c7a507aa5cf50be2fd372536ebfc81 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Wed, 17 Apr 2019 16:59:05 +0700
Subject: [PATCH 003/202] Resolve conflicts
---
config/config.exs | 10 ++++++++
lib/pleroma/user.ex | 2 ++
mix.exs | 5 ++--
mix.lock | 51 +++++++++++++++++++++-----------------
test/notification_test.exs | 22 ++++++++++------
5 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/config/config.exs b/config/config.exs
index 595e3505c..747d33884 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -464,6 +464,16 @@
total_user_limit: 300,
enabled: true
+config :pleroma, :email_notifications,
+ digest: %{
+ # When to send digest email, in crontab format (https://en.wikipedia.org/wiki/Cron)
+ schedule: "0 0 * * 0",
+ # Minimum interval between digest emails to one user
+ interval: 7,
+ # Minimum user inactivity threshold
+ inactivity_threshold: 7
+ }
+
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 78eb29ddd..0982f6ed8 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -55,6 +55,8 @@ defmodule Pleroma.User do
field(:tags, {:array, :string}, default: [])
field(:bookmarks, {:array, :string}, default: [])
field(:last_refreshed_at, :naive_datetime_usec)
+ field(:current_sign_in_at, :naive_datetime)
+ field(:last_digest_emailed_at, :naive_datetime)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
embeds_one(:info, Pleroma.User.Info)
diff --git a/mix.exs b/mix.exs
index 15e182239..da2e284f8 100644
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ def project do
elixir: "~> 1.7",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
- elixirc_options: [warnings_as_errors: true],
+ # elixirc_options: [warnings_as_errors: true],
xref: [exclude: [:eldap]],
start_permanent: Mix.env() == :prod,
aliases: aliases(),
@@ -110,7 +110,8 @@ defp deps do
{:prometheus_ecto, "~> 1.4"},
{:prometheus_process_collector, "~> 1.4"},
{:recon, github: "ferd/recon", tag: "2.4.0"},
- {:quack, "~> 0.1.1"}
+ {:quack, "~> 0.1.1"},
+ {:quantum, "~> 2.3"}
] ++ oauth_deps
end
diff --git a/mix.lock b/mix.lock
index d494cc82d..6e322240a 100644
--- a/mix.lock
+++ b/mix.lock
@@ -3,23 +3,24 @@
"auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "90613b4bae875a3610c275b7056b61ffdd53210d", [ref: "90613b4bae875a3610c275b7056b61ffdd53210d"]},
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
- "cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
- "calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
+ "cachex": {:hex, :cachex, "3.0.3", "4e2d3e05814a5738f5ff3903151d5c25636d72a3527251b753f501ad9c657967", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
+ "calendar": {:hex, :calendar, "0.17.5", "0ff5b09a60b9677683aa2a6fee948558660501c74a289103ea099806bc41a352", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"},
- "comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
+ "comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
- "cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
- "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
+ "cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
+ "cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
+ "crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
- "db_connection": {:hex, :db_connection, "2.0.5", "ddb2ba6761a08b2bb9ca0e7d260e8f4dd39067426d835c24491a321b7f92a4da", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
+ "db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
- "ecto": {:hex, :ecto, "3.0.7", "44dda84ac6b17bbbdeb8ac5dfef08b7da253b37a453c34ab1a98de7f7e5fec7f", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
+ "ecto": {:hex, :ecto, "3.0.8", "9eb6a1fcfc593e6619d45ef51afe607f1554c21ca188a1cd48eecc27223567f1", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"ecto_sql": {:hex, :ecto_sql, "3.0.5", "7e44172b4f7aca4469f38d7f6a3da394dbf43a1bcf0ca975e958cb957becd74e", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.6", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
- "eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"},
+ "eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
"ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
@@ -27,57 +28,61 @@
"ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"},
- "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"},
+ "gen_stage": {:hex, :gen_stage, "0.14.1", "9d46723fda072d4f4bb31a102560013f7960f5d80ea44dcb96fd6304ed61e7a4", [:mix], [], "hexpm"},
+ "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
+ "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
- "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
+ "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
+ "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
- "mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], [], "hexpm"},
- "mock": {:hex, :mock, "0.3.1", "994f00150f79a0ea50dc9d86134cd9ebd0d177ad60bd04d1e46336cdfdb98ff9", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
+ "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
+ "mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
- "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"},
- "phoenix": {:hex, :phoenix, "1.4.1", "801f9d632808657f1f7c657c8bbe624caaf2ba91429123ebe3801598aea4c3d9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
+ "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm"},
+ "phoenix": {:hex, :phoenix, "1.4.3", "8eed4a64ff1e12372cd634724bddd69185938f52c18e1396ebac76375d85677d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
- "phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
- "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"},
+ "phoenix_html": {:hex, :phoenix_html, "2.13.2", "f5d27c9b10ce881a60177d2b5227314fc60881e6b66b41dfe3349db6ed06cf57", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+ "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
- "plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+ "plug_cowboy": {:hex, :plug_cowboy, "2.0.2", "6055f16868cc4882b24b6e1d63d2bada94fb4978413377a3b32ac16c18dffba2", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
- "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
- "postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+ "postgrex": {:hex, :postgrex, "0.14.2", "6680591bbce28d92f043249205e8b01b36cab9ef2a7911abc43649242e1a3b78", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"},
"prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
- "prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
+ "prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.3", "657386e8f142fc817347d95c1f3a05ab08710f7df9e7f86db6facaed107ed929", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
+ "quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
- "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
+ "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"},
+ "swoosh": {:hex, :swoosh, "0.23.1", "209b7cc6d862c09d2a064c16caa4d4d1c9c936285476459e16591e0065f8432b", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.3.0", "099a7f3ce31e4780f971b4630a3c22ec66d22208bc090fe33a2a3a6a67754a73", [:rebar3], [], "hexpm"},
"tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"timex": {:hex, :timex, "3.5.0", "b0a23167da02d0fe4f1a4e104d1f929a00d348502b52432c05de875d0b9cffa5", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
- "tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
+ "tzdata": {:hex, :tzdata, "0.5.20", "304b9e98a02840fb32a43ec111ffbe517863c8566eb04a061f1c4dbb90b4d84c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
- "unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
+ "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},
"web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
}
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 907b9e669..27d8cace7 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -4,12 +4,15 @@
defmodule Pleroma.NotificationTest do
use Pleroma.DataCase
+
+ import Pleroma.Factory
+ import Mock
+
alias Pleroma.Notification
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.TwitterAPI.TwitterAPI
- import Pleroma.Factory
describe "create_notifications" do
test "notifies someone when they are directly addressed" do
@@ -312,16 +315,19 @@ test "Updates `updated_at` field" do
})
end)
- Process.sleep(1000)
-
[notification | _] = Notification.for_user(user2)
- Notification.set_read_up_to(user2, notification.id)
+ utc_now = NaiveDateTime.utc_now()
+ future = NaiveDateTime.add(utc_now, 5, :second)
- Notification.for_user(user2)
- |> Enum.each(fn notification ->
- assert notification.updated_at > notification.inserted_at
- end)
+ with_mock NaiveDateTime, utc_now: fn -> future end do
+ Notification.set_read_up_to(user2, notification.id)
+
+ Notification.for_user(user2)
+ |> Enum.each(fn notification ->
+ assert notification.updated_at > notification.inserted_at
+ end)
+ end
end
end
From aeafa0b2ef996f15f9ff4a6ade70a693b12b208f Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Fri, 19 Apr 2019 22:16:17 +0700
Subject: [PATCH 004/202] Add Notification.for_user_since/2
---
config/config.exs | 1 +
lib/pleroma/notification.ex | 21 +++++++++++++++++
test/notification_test.exs | 45 +++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
diff --git a/config/config.exs b/config/config.exs
index 747d33884..c452b728b 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -467,6 +467,7 @@
config :pleroma, :email_notifications,
digest: %{
# When to send digest email, in crontab format (https://en.wikipedia.org/wiki/Cron)
+ # 0 0 * * 0 - once a week at midnight on Sunday morning
schedule: "0 0 * * 0",
# Minimum interval between digest emails to one user
interval: 7,
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 29845b9da..d79f0f563 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -17,6 +17,8 @@ defmodule Pleroma.Notification do
import Ecto.Query
import Ecto.Changeset
+ @type t :: %__MODULE__{}
+
schema "notifications" do
field(:seen, :boolean, default: false)
belongs_to(:user, User, type: Pleroma.FlakeId)
@@ -51,6 +53,25 @@ def for_user(user, opts \\ %{}) do
|> Pagination.fetch_paginated(opts)
end
+ @doc """
+ Returns notifications for user received since given date.
+
+ ## Examples
+
+ iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-13 11:22:33])
+ [%Pleroma.Notification{}, %Pleroma.Notification{}]
+
+ iex> Pleroma.Notification.for_user_since(%Pleroma.User{}, ~N[2019-04-15 11:22:33])
+ []
+ """
+ @spec for_user_since(Pleroma.User.t(), NaiveDateTime.t()) :: [t()]
+ def for_user_since(user, date) do
+ from(n in for_user_query(user),
+ where: n.updated_at > ^date
+ )
+ |> Repo.all()
+ end
+
def set_read_up_to(%{id: user_id} = _user, id) do
query =
from(
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 27d8cace7..dbc4f48f6 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -331,6 +331,51 @@ test "Updates `updated_at` field" do
end
end
+ describe "for_user_since/2" do
+ defp days_ago(days) do
+ NaiveDateTime.add(
+ NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
+ -days * 60 * 60 * 24,
+ :second
+ )
+ end
+
+ test "Returns recent notifications" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+
+ Enum.each(0..10, fn i ->
+ {:ok, _activity} =
+ CommonAPI.post(user1, %{
+ "status" => "hey ##{i} @#{user2.nickname}!"
+ })
+ end)
+
+ {old, new} = Enum.split(Notification.for_user(user2), 5)
+
+ Enum.each(old, fn notification ->
+ notification
+ |> cast(%{updated_at: days_ago(10)}, [:updated_at])
+ |> Pleroma.Repo.update!()
+ end)
+
+ recent_notifications_ids =
+ user2
+ |> Notification.for_user_since(
+ NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86400, :second)
+ )
+ |> Enum.map(& &1.id)
+
+ Enum.each(old, fn %{id: id} ->
+ refute id in recent_notifications_ids
+ end)
+
+ Enum.each(new, fn %{id: id} ->
+ assert id in recent_notifications_ids
+ end)
+ end
+ end
+
describe "notification target determination" do
test "it sends notifications to addressed users in new messages" do
user = insert(:user)
From 8add1194448cfc183dce01b86451422195d44023 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Fri, 19 Apr 2019 22:17:54 +0700
Subject: [PATCH 005/202] Add User.list_inactive_users_query/1
---
lib/pleroma/user.ex | 38 +++++++
...d_signin_and_last_digest_dates_to_user.exs | 9 ++
test/user_test.exs | 103 ++++++++++++++++++
3 files changed, 150 insertions(+)
create mode 100644 priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 0982f6ed8..c67a7b7a1 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -1447,4 +1447,42 @@ defp paginate(query, page, page_size) do
def showing_reblogs?(%User{} = user, %User{} = target) do
target.ap_id not in user.info.muted_reblogs
end
+
+ @doc """
+ The function returns a query to get users with no activity for given interval of days.
+ Inactive users are those who didn't read any notification, or had any activity where
+ the user is the activity's actor, during `inactivity_threshold` days.
+ Deactivated users will not appear in this list.
+
+ ## Examples
+
+ iex> Pleroma.User.list_inactive_users()
+ %Ecto.Query{}
+ """
+ @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
+ def list_inactive_users_query(inactivity_threshold \\ 7) do
+ negative_inactivity_threshold = -inactivity_threshold
+ now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+ # Subqueries are not supported in `where` clauses, join gets too complicated.
+ has_read_notifications =
+ from(n in Pleroma.Notification,
+ where: n.seen == true,
+ group_by: n.id,
+ having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
+ select: n.user_id
+ )
+ |> Pleroma.Repo.all()
+
+ from(u in Pleroma.User,
+ left_join: a in Pleroma.Activity,
+ on: u.ap_id == a.actor,
+ where: not is_nil(u.nickname),
+ where: fragment("not (?->'deactivated' @> 'true')", u.info),
+ where: u.id not in ^has_read_notifications,
+ group_by: u.id,
+ having:
+ max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
+ is_nil(max(a.inserted_at))
+ )
+ end
end
diff --git a/priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs b/priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs
new file mode 100644
index 000000000..4312b171f
--- /dev/null
+++ b/priv/repo/migrations/20190413085040_add_signin_and_last_digest_dates_to_user.exs
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddSigninAndLastDigestDatesToUser do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add(:last_digest_emailed_at, :naive_datetime, default: fragment("now()"))
+ end
+ end
+end
diff --git a/test/user_test.exs b/test/user_test.exs
index d2167a970..ba02997dc 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -1167,4 +1167,107 @@ test "follower count is updated when a follower is blocked" do
assert Map.get(user_show, "followers_count") == 2
end
+
+ describe "list_inactive_users_query/1" do
+ defp days_ago(days) do
+ NaiveDateTime.add(
+ NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
+ -days * 60 * 60 * 24,
+ :second
+ )
+ end
+
+ test "Users are inactive by default" do
+ total = 10
+
+ users =
+ Enum.map(1..total, fn _ ->
+ insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+ end)
+
+ inactive_users_ids =
+ Pleroma.User.list_inactive_users_query()
+ |> Pleroma.Repo.all()
+ |> Enum.map(& &1.id)
+
+ Enum.each(users, fn user ->
+ assert user.id in inactive_users_ids
+ end)
+ end
+
+ test "Only includes users who has no recent activity" do
+ total = 10
+
+ users =
+ Enum.map(1..total, fn _ ->
+ insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+ end)
+
+ {inactive, active} = Enum.split(users, trunc(total / 2))
+
+ Enum.map(active, fn user ->
+ to = Enum.random(users -- [user])
+
+ {:ok, _} =
+ Pleroma.Web.TwitterAPI.TwitterAPI.create_status(user, %{
+ "status" => "hey @#{to.nickname}"
+ })
+ end)
+
+ inactive_users_ids =
+ Pleroma.User.list_inactive_users_query()
+ |> Pleroma.Repo.all()
+ |> Enum.map(& &1.id)
+
+ Enum.each(active, fn user ->
+ refute user.id in inactive_users_ids
+ end)
+
+ Enum.each(inactive, fn user ->
+ assert user.id in inactive_users_ids
+ end)
+ end
+
+ test "Only includes users with no read notifications" do
+ total = 10
+
+ users =
+ Enum.map(1..total, fn _ ->
+ insert(:user, last_digest_emailed_at: days_ago(20), info: %{deactivated: false})
+ end)
+
+ [sender | recipients] = users
+ {inactive, active} = Enum.split(recipients, trunc(total / 2))
+
+ Enum.each(recipients, fn to ->
+ {:ok, _} =
+ Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
+ "status" => "hey @#{to.nickname}"
+ })
+
+ {:ok, _} =
+ Pleroma.Web.TwitterAPI.TwitterAPI.create_status(sender, %{
+ "status" => "hey again @#{to.nickname}"
+ })
+ end)
+
+ Enum.each(active, fn user ->
+ [n1, _n2] = Pleroma.Notification.for_user(user)
+ {:ok, _} = Pleroma.Notification.read_one(user, n1.id)
+ end)
+
+ inactive_users_ids =
+ Pleroma.User.list_inactive_users_query()
+ |> Pleroma.Repo.all()
+ |> Enum.map(& &1.id)
+
+ Enum.each(active, fn user ->
+ refute user.id in inactive_users_ids
+ end)
+
+ Enum.each(inactive, fn user ->
+ assert user.id in inactive_users_ids
+ end)
+ end
+ end
end
From bc7862106d9881f858a58319e9e4b44cba1bcf01 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Fri, 19 Apr 2019 23:26:41 +0700
Subject: [PATCH 006/202] Fix tests
---
lib/pleroma/user.ex | 1 -
test/notification_test.exs | 27 ---------------------------
test/support/builders/user_builder.ex | 3 ++-
test/support/factory.ex | 3 ++-
4 files changed, 4 insertions(+), 30 deletions(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index c67a7b7a1..7053dfaf3 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -55,7 +55,6 @@ defmodule Pleroma.User do
field(:tags, {:array, :string}, default: [])
field(:bookmarks, {:array, :string}, default: [])
field(:last_refreshed_at, :naive_datetime_usec)
- field(:current_sign_in_at, :naive_datetime)
field(:last_digest_emailed_at, :naive_datetime)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
diff --git a/test/notification_test.exs b/test/notification_test.exs
index dbc4f48f6..462398d75 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -6,7 +6,6 @@ defmodule Pleroma.NotificationTest do
use Pleroma.DataCase
import Pleroma.Factory
- import Mock
alias Pleroma.Notification
alias Pleroma.User
@@ -303,32 +302,6 @@ test "it sets all notifications as read up to a specified notification ID" do
assert n2.seen == true
assert n3.seen == false
end
-
- test "Updates `updated_at` field" do
- user1 = insert(:user)
- user2 = insert(:user)
-
- Enum.each(0..10, fn i ->
- {:ok, _activity} =
- TwitterAPI.create_status(user1, %{
- "status" => "#{i} hi @#{user2.nickname}"
- })
- end)
-
- [notification | _] = Notification.for_user(user2)
-
- utc_now = NaiveDateTime.utc_now()
- future = NaiveDateTime.add(utc_now, 5, :second)
-
- with_mock NaiveDateTime, utc_now: fn -> future end do
- Notification.set_read_up_to(user2, notification.id)
-
- Notification.for_user(user2)
- |> Enum.each(fn notification ->
- assert notification.updated_at > notification.inserted_at
- end)
- end
- end
end
describe "for_user_since/2" do
diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex
index f58e1b0ad..6da16f71a 100644
--- a/test/support/builders/user_builder.ex
+++ b/test/support/builders/user_builder.ex
@@ -9,7 +9,8 @@ def build(data \\ %{}) do
nickname: "testname",
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: "A tester.",
- ap_id: "some id"
+ ap_id: "some id",
+ last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
}
Map.merge(user, data)
diff --git a/test/support/factory.ex b/test/support/factory.ex
index ea59912cf..0840f31ec 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -12,7 +12,8 @@ def user_factory do
nickname: sequence(:nickname, &"nick#{&1}"),
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: sequence(:bio, &"Tester Number #{&1}"),
- info: %{}
+ info: %{},
+ last_digest_emailed_at: NaiveDateTime.utc_now()
}
%{
From 64a2c6a041ca62ad84b1d682ef56fbca45e44de5 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sat, 20 Apr 2019 19:42:19 +0700
Subject: [PATCH 007/202] Digest emails
---
config/config.exs | 2 +
lib/mix/tasks/pleroma/instance.ex | 2 +
lib/mix/tasks/pleroma/sample_config.eex | 2 +
lib/pleroma/application.ex | 22 ++++++-
lib/pleroma/digest_email_worker.ex | 45 ++++++++++++++
lib/pleroma/emails/user_email.ex | 59 ++++++++++++++++++-
lib/pleroma/jwt.ex | 9 +++
lib/pleroma/quantum_scheduler.ex | 4 ++
lib/pleroma/user.ex | 36 +++++++++++
.../web/mailer/subscription_controller.ex | 18 ++++++
lib/pleroma/web/router.ex | 2 +
.../web/templates/email/digest.html.eex | 20 +++++++
.../web/templates/layout/email.html.eex | 10 ++++
.../subscription/unsubscribe_failure.html.eex | 1 +
.../subscription/unsubscribe_success.html.eex | 1 +
lib/pleroma/web/views/email_view.ex | 5 ++
.../web/views/mailer/subscription_view.ex | 3 +
mix.exs | 4 +-
mix.lock | 2 +
19 files changed, 243 insertions(+), 4 deletions(-)
create mode 100644 lib/pleroma/digest_email_worker.ex
create mode 100644 lib/pleroma/jwt.ex
create mode 100644 lib/pleroma/quantum_scheduler.ex
create mode 100644 lib/pleroma/web/mailer/subscription_controller.ex
create mode 100644 lib/pleroma/web/templates/email/digest.html.eex
create mode 100644 lib/pleroma/web/templates/layout/email.html.eex
create mode 100644 lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
create mode 100644 lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
create mode 100644 lib/pleroma/web/views/email_view.ex
create mode 100644 lib/pleroma/web/views/mailer/subscription_view.ex
diff --git a/config/config.exs b/config/config.exs
index 25dc91eb1..2663b1ebd 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -468,6 +468,8 @@
config :pleroma, :email_notifications,
digest: %{
+ # Globally enable or disable digest emails
+ active: true,
# When to send digest email, in crontab format (https://en.wikipedia.org/wiki/Cron)
# 0 0 * * 0 - once a week at midnight on Sunday morning
schedule: "0 0 * * 0",
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index 6cee8d630..d276df93a 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -125,6 +125,7 @@ def run(["gen" | rest]) do
)
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
+ jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
@@ -142,6 +143,7 @@ def run(["gen" | rest]) do
dbpass: dbpass,
version: Pleroma.Mixfile.project() |> Keyword.get(:version),
secret: secret,
+ jwt_secret: jwt_secret,
signing_salt: signing_salt,
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
diff --git a/lib/mix/tasks/pleroma/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex
index 52bd57cb7..ec7d8821e 100644
--- a/lib/mix/tasks/pleroma/sample_config.eex
+++ b/lib/mix/tasks/pleroma/sample_config.eex
@@ -76,3 +76,5 @@ config :web_push_encryption, :vapid_details,
# storage_url: "https://swift-endpoint.prodider.com/v1/AUTH_/",
# object_url: "https://cdn-endpoint.provider.com/"
#
+
+config :joken, default_signer: "<%= jwt_secret %>"
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index eeb415084..76f8d9bcd 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -105,7 +105,8 @@ def start(_type, _args) do
id: :cachex_idem
),
worker(Pleroma.FlakeId, []),
- worker(Pleroma.ScheduledActivityWorker, [])
+ worker(Pleroma.ScheduledActivityWorker, []),
+ worker(Pleroma.QuantumScheduler, [])
] ++
hackney_pool_children() ++
[
@@ -125,7 +126,9 @@ def start(_type, _args) do
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Pleroma.Supervisor]
- Supervisor.start_link(children, opts)
+ result = Supervisor.start_link(children, opts)
+ :ok = after_supervisor_start()
+ result
end
defp setup_instrumenters do
@@ -183,4 +186,19 @@ defp hackney_pool_children do
:hackney_pool.child_spec(pool, options)
end
end
+
+ defp after_supervisor_start() do
+ with digest_config <- Application.get_env(:pleroma, :email_notifications)[:digest],
+ true <- digest_config[:active],
+ %Crontab.CronExpression{} = schedule <-
+ Crontab.CronExpression.Parser.parse!(digest_config[:schedule]) do
+ Pleroma.QuantumScheduler.new_job()
+ |> Quantum.Job.set_name(:digest_emails)
+ |> Quantum.Job.set_schedule(schedule)
+ |> Quantum.Job.set_task(&Pleroma.DigestEmailWorker.run/0)
+ |> Pleroma.QuantumScheduler.add_job()
+ end
+
+ :ok
+ end
end
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
new file mode 100644
index 000000000..fa6067a03
--- /dev/null
+++ b/lib/pleroma/digest_email_worker.ex
@@ -0,0 +1,45 @@
+defmodule Pleroma.DigestEmailWorker do
+ import Ecto.Query
+ require Logger
+
+ # alias Pleroma.User
+
+ def run() do
+ Logger.warn("Running digester")
+ config = Application.get_env(:pleroma, :email_notifications)[:digest]
+ negative_interval = -Map.fetch!(config, :interval)
+ inactivity_threshold = Map.fetch!(config, :inactivity_threshold)
+ inactive_users_query = Pleroma.User.list_inactive_users_query(inactivity_threshold)
+
+ now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+
+ from(u in inactive_users_query,
+ where: fragment("? #> '{\"email_notifications\",\"digest\"}' @> 'true'", u.info),
+ where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
+ select: u
+ )
+ |> Pleroma.Repo.all()
+ |> run(:pre)
+ end
+
+ defp run(v, :pre) do
+ Logger.warn("Running for #{length(v)} users")
+ run(v)
+ end
+
+ defp run([]), do: :ok
+
+ defp run([user | users]) do
+ with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(user) do
+ Logger.warn("Sending to #{user.nickname}")
+ Pleroma.Emails.Mailer.deliver_async(email)
+ else
+ _ ->
+ Logger.warn("Skipping #{user.nickname}")
+ end
+
+ Pleroma.User.touch_last_digest_emailed_at(user)
+
+ run(users)
+ end
+end
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index 8502a0d0c..64f855112 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -5,7 +5,7 @@
defmodule Pleroma.Emails.UserEmail do
@moduledoc "User emails"
- import Swoosh.Email
+ use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router
@@ -92,4 +92,61 @@ def account_confirmation_email(user) do
|> subject("#{instance_name()} account confirmation")
|> html_body(html_body)
end
+
+ @doc """
+ Email used in digest email notifications
+ Includes Mentions and New Followers data
+ If there are no mentions (even when new followers exist), the function will return nil
+ """
+ @spec digest_email(Pleroma.User.t()) :: Swoosh.Email.t() | nil
+ def digest_email(user) do
+ new_notifications =
+ Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
+ |> Enum.reduce(%{followers: [], mentions: []}, fn
+ %{activity: %{data: %{"type" => "Create"}, actor: actor}} = notification, acc ->
+ new_mention = %{data: notification, from: Pleroma.User.get_by_ap_id(actor)}
+ %{acc | mentions: [new_mention | acc.mentions]}
+
+ %{activity: %{data: %{"type" => "Follow"}, actor: actor}} = notification, acc ->
+ new_follower = %{data: notification, from: Pleroma.User.get_by_ap_id(actor)}
+ %{acc | followers: [new_follower | acc.followers]}
+
+ _, acc ->
+ acc
+ end)
+
+ with [_ | _] = mentions <- new_notifications.mentions do
+ html_data = %{
+ instance: instance_name(),
+ user: user,
+ mentions: mentions,
+ followers: new_notifications.followers,
+ unsubscribe_link: unsubscribe_url(user, "digest")
+ }
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject("Your digest from #{instance_name()}")
+ |> render_body("digest.html", html_data)
+ else
+ _ ->
+ nil
+ end
+ end
+
+ @doc """
+ Generate unsubscribe link for given user and notifications type.
+ The link contains JWT token with the data, and subscription can be modified without
+ authorization.
+ """
+ @spec unsubscribe_url(Pleroma.User.t(), String.t()) :: String.t()
+ def unsubscribe_url(user, notifications_type) do
+ token =
+ %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
+ |> Pleroma.JWT.generate_and_sign!()
+ |> Base.encode64()
+
+ Router.Helpers.subscription_url(Pleroma.Web.Endpoint, :unsubscribe, token)
+ end
end
diff --git a/lib/pleroma/jwt.ex b/lib/pleroma/jwt.ex
new file mode 100644
index 000000000..10102ff5d
--- /dev/null
+++ b/lib/pleroma/jwt.ex
@@ -0,0 +1,9 @@
+defmodule Pleroma.JWT do
+ use Joken.Config
+
+ @impl true
+ def token_config do
+ default_claims(skip: [:aud])
+ |> add_claim("aud", &Pleroma.Web.Endpoint.url/0, &(&1 == Pleroma.Web.Endpoint.url()))
+ end
+end
diff --git a/lib/pleroma/quantum_scheduler.ex b/lib/pleroma/quantum_scheduler.ex
new file mode 100644
index 000000000..9a3df81f6
--- /dev/null
+++ b/lib/pleroma/quantum_scheduler.ex
@@ -0,0 +1,4 @@
+defmodule Pleroma.QuantumScheduler do
+ use Quantum.Scheduler,
+ otp_app: :pleroma
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 7053dfaf3..2509d2366 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -1484,4 +1484,40 @@ def list_inactive_users_query(inactivity_threshold \\ 7) do
is_nil(max(a.inserted_at))
)
end
+
+ @doc """
+ Enable or disable email notifications for user
+
+ ## Examples
+
+ iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true)
+ Pleroma.User{info: %{email_notifications: %{"digest" => true}}}
+
+ iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false)
+ Pleroma.User{info: %{email_notifications: %{"digest" => false}}}
+ """
+ @spec switch_email_notifications(t(), String.t(), boolean()) ::
+ {:ok, t()} | {:error, Ecto.Changeset.t()}
+ def switch_email_notifications(user, type, status) do
+ info = Pleroma.User.Info.update_email_notifications(user.info, %{type => status})
+
+ change(user)
+ |> put_embed(:info, info)
+ |> update_and_set_cache()
+ end
+
+ @doc """
+ Set `last_digest_emailed_at` value for the user to current time
+ """
+ @spec touch_last_digest_emailed_at(t()) :: t()
+ def touch_last_digest_emailed_at(user) do
+ now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+
+ {:ok, updated_user} =
+ user
+ |> change(%{last_digest_emailed_at: now})
+ |> update_and_set_cache()
+
+ updated_user
+ end
end
diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex
new file mode 100644
index 000000000..2334ebacb
--- /dev/null
+++ b/lib/pleroma/web/mailer/subscription_controller.ex
@@ -0,0 +1,18 @@
+defmodule Pleroma.Web.Mailer.SubscriptionController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.{JWT, Repo, User}
+
+ def unsubscribe(conn, %{"token" => encoded_token}) do
+ with {:ok, token} <- Base.decode64(encoded_token),
+ {:ok, claims} <- JWT.verify_and_validate(token),
+ %{"act" => %{"unsubscribe" => type}, "sub" => uid} <- claims,
+ %User{} = user <- Repo.get(User, uid),
+ {:ok, _user} <- User.switch_email_notifications(user, type, false) do
+ render(conn, "unsubscribe_success.html", email: user.email)
+ else
+ _err ->
+ render(conn, "unsubscribe_failure.html")
+ end
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 8b665d61b..09e51e602 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -562,6 +562,8 @@ defmodule Pleroma.Web.Router do
post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
+
+ get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
end
scope "/", Pleroma.Web do
diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex
new file mode 100644
index 000000000..93c9c884f
--- /dev/null
+++ b/lib/pleroma/web/templates/email/digest.html.eex
@@ -0,0 +1,20 @@
+Hey <%= @user.nickname %>, here is what you've missed!
+
+New Mentions:
+
+<%= for %{data: mention, from: from} <- @mentions do %>
+ <%= link from.nickname, to: mention.activity.actor %>: <%= raw mention.activity.object.data["content"] %>
+<% end %>
+
+
+<%= if @followers != [] do %>
+<%= length(@followers) %> New Followers:
+
+<%= for %{data: follow, from: from} <- @followers do %>
+ <%= link from.nickname, to: follow.activity.actor %>
+<% end %>
+
+<% end %>
+
+You have received this email because you have signed up to receive digest emails from <%= @instance %> Pleroma instance.
+The email address you are subscribed as is <%= @user.email %>. To unsubscribe, please go <%= link "here", to: @unsubscribe_link %>.
\ No newline at end of file
diff --git a/lib/pleroma/web/templates/layout/email.html.eex b/lib/pleroma/web/templates/layout/email.html.eex
new file mode 100644
index 000000000..f6dcd7f0f
--- /dev/null
+++ b/lib/pleroma/web/templates/layout/email.html.eex
@@ -0,0 +1,10 @@
+
+
+
+
+ <%= @email.subject %>
+
+
+ <%= render @view_module, @view_template, assigns %>
+
+
\ No newline at end of file
diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
new file mode 100644
index 000000000..7b476f02d
--- /dev/null
+++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
@@ -0,0 +1 @@
+UNSUBSCRIBE FAILURE
diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
new file mode 100644
index 000000000..6dfa2c185
--- /dev/null
+++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
@@ -0,0 +1 @@
+UNSUBSCRIBE SUCCESSFUL
diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex
new file mode 100644
index 000000000..b63eb162c
--- /dev/null
+++ b/lib/pleroma/web/views/email_view.ex
@@ -0,0 +1,5 @@
+defmodule Pleroma.Web.EmailView do
+ use Pleroma.Web, :view
+ import Phoenix.HTML
+ import Phoenix.HTML.Link
+end
diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex
new file mode 100644
index 000000000..fc3d20816
--- /dev/null
+++ b/lib/pleroma/web/views/mailer/subscription_view.ex
@@ -0,0 +1,3 @@
+defmodule Pleroma.Web.Mailer.SubscriptionView do
+ use Pleroma.Web, :view
+end
diff --git a/mix.exs b/mix.exs
index da2e284f8..6bb105538 100644
--- a/mix.exs
+++ b/mix.exs
@@ -93,6 +93,7 @@ defp deps do
{:ex_doc, "~> 0.20.2", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"},
{:swoosh, "~> 0.20"},
+ {:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
{:floki, "~> 0.20.0"},
@@ -111,7 +112,8 @@ defp deps do
{:prometheus_process_collector, "~> 1.4"},
{:recon, github: "ferd/recon", tag: "2.4.0"},
{:quack, "~> 0.1.1"},
- {:quantum, "~> 2.3"}
+ {:quantum, "~> 2.3"},
+ {:joken, "~> 2.0"}
] ++ oauth_deps
end
diff --git a/mix.lock b/mix.lock
index 6e322240a..73aed012f 100644
--- a/mix.lock
+++ b/mix.lock
@@ -37,6 +37,7 @@
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
+ "joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"},
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
@@ -55,6 +56,7 @@
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_html": {:hex, :phoenix_html, "2.13.2", "f5d27c9b10ce881a60177d2b5227314fc60881e6b66b41dfe3349db6ed06cf57", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
+ "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.2.0", "a7e0b32077cd6d2323ae15198839b05d9caddfa20663fd85787479e81f89520e", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 0.1", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm"},
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.7.2", "d7b7db7fbd755e8283b6c0a50be71ec0a3d67d9213d74422d9372effc8e87fd1", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "2.0.2", "6055f16868cc4882b24b6e1d63d2bada94fb4978413377a3b32ac16c18dffba2", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
From 05cdb2f2389376081973d96b32e876d2a032d1f1 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sat, 20 Apr 2019 19:42:50 +0700
Subject: [PATCH 008/202] Do not track coverage files
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 774893b35..8166e65e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,5 @@ erl_crash.dump
# Prevent committing docs files
/priv/static/doc/*
+
+/cover
From 724311e15177a1a97f533f11ff17d8d0146800ef Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sat, 20 Apr 2019 19:57:43 +0700
Subject: [PATCH 009/202] Fix Credo warnings
---
lib/pleroma/application.ex | 2 +-
lib/pleroma/digest_email_worker.ex | 4 ++--
lib/pleroma/web/mailer/subscription_controller.ex | 4 +++-
mix.exs | 2 +-
test/notification_test.exs | 2 +-
5 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 76f8d9bcd..299f8807b 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -187,7 +187,7 @@ defp hackney_pool_children do
end
end
- defp after_supervisor_start() do
+ defp after_supervisor_start do
with digest_config <- Application.get_env(:pleroma, :email_notifications)[:digest],
true <- digest_config[:active],
%Crontab.CronExpression{} = schedule <-
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index fa6067a03..7be470f5f 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -4,7 +4,7 @@ defmodule Pleroma.DigestEmailWorker do
# alias Pleroma.User
- def run() do
+ def run do
Logger.warn("Running digester")
config = Application.get_env(:pleroma, :email_notifications)[:digest]
negative_interval = -Map.fetch!(config, :interval)
@@ -14,7 +14,7 @@ def run() do
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
from(u in inactive_users_query,
- where: fragment("? #> '{\"email_notifications\",\"digest\"}' @> 'true'", u.info),
+ where: fragment(~s(? #> '{"email_notifications","digest"}' @> 'true'), u.info),
where: u.last_digest_emailed_at < datetime_add(^now, ^negative_interval, "day"),
select: u
)
diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex
index 2334ebacb..478a83518 100644
--- a/lib/pleroma/web/mailer/subscription_controller.ex
+++ b/lib/pleroma/web/mailer/subscription_controller.ex
@@ -1,7 +1,9 @@
defmodule Pleroma.Web.Mailer.SubscriptionController do
use Pleroma.Web, :controller
- alias Pleroma.{JWT, Repo, User}
+ alias Pleroma.JWT
+ alias Pleroma.Repo
+ alias Pleroma.User
def unsubscribe(conn, %{"token" => encoded_token}) do
with {:ok, token} <- Base.decode64(encoded_token),
diff --git a/mix.exs b/mix.exs
index 6bb105538..2cdfb1392 100644
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ def project do
elixir: "~> 1.7",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
- # elixirc_options: [warnings_as_errors: true],
+ elixirc_options: [warnings_as_errors: true],
xref: [exclude: [:eldap]],
start_permanent: Mix.env() == :prod,
aliases: aliases(),
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 462398d75..3bbce8fcf 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -335,7 +335,7 @@ test "Returns recent notifications" do
recent_notifications_ids =
user2
|> Notification.for_user_since(
- NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86400, :second)
+ NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
)
|> Enum.map(& &1.id)
From 2359ee38b38de17df4dfe9cbdfe551bb7d9a034d Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 21 Apr 2019 16:36:25 +0700
Subject: [PATCH 010/202] Set digest emails to false by default
---
lib/pleroma/user/info.ex | 2 +-
priv/repo/migrations/20190412052952_add_user_info_fields.exs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 194dd5581..d827293b8 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -42,7 +42,7 @@ defmodule Pleroma.User.Info do
field(:hide_follows, :boolean, default: false)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
- field(:email_notifications, :map, default: %{"digest" => true})
+ field(:email_notifications, :map, default: %{"digest" => false})
field(:notification_settings, :map,
default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
diff --git a/priv/repo/migrations/20190412052952_add_user_info_fields.exs b/priv/repo/migrations/20190412052952_add_user_info_fields.exs
index 203d0fc3b..646c91f32 100644
--- a/priv/repo/migrations/20190412052952_add_user_info_fields.exs
+++ b/priv/repo/migrations/20190412052952_add_user_info_fields.exs
@@ -6,7 +6,7 @@ def up do
UPDATE users
SET info = info || '{
\"email_notifications\": {
- \"digest\": true
+ \"digest\": false
}
}'")
end
From f1d90ee94206db00025d41b13a2906aa30d748f0 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 21 Apr 2019 16:40:05 +0700
Subject: [PATCH 011/202] Remove debug code
---
lib/pleroma/digest_email_worker.ex | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index 7be470f5f..65013f77e 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -1,11 +1,7 @@
defmodule Pleroma.DigestEmailWorker do
import Ecto.Query
- require Logger
-
- # alias Pleroma.User
def run do
- Logger.warn("Running digester")
config = Application.get_env(:pleroma, :email_notifications)[:digest]
negative_interval = -Map.fetch!(config, :interval)
inactivity_threshold = Map.fetch!(config, :inactivity_threshold)
@@ -19,23 +15,14 @@ def run do
select: u
)
|> Pleroma.Repo.all()
- |> run(:pre)
- end
-
- defp run(v, :pre) do
- Logger.warn("Running for #{length(v)} users")
- run(v)
+ |> run()
end
defp run([]), do: :ok
defp run([user | users]) do
with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(user) do
- Logger.warn("Sending to #{user.nickname}")
Pleroma.Emails.Mailer.deliver_async(email)
- else
- _ ->
- Logger.warn("Skipping #{user.nickname}")
end
Pleroma.User.touch_last_digest_emailed_at(user)
From b87ad13803df59d88feb736c3d0ff9cf514989d7 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 21 Apr 2019 19:36:31 +0700
Subject: [PATCH 012/202] Move comments for email_notifications config to docs
---
config/config.exs | 5 -----
docs/config.md | 12 ++++++++++++
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/config/config.exs b/config/config.exs
index 2663b1ebd..b1d506b59 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -468,14 +468,9 @@
config :pleroma, :email_notifications,
digest: %{
- # Globally enable or disable digest emails
active: true,
- # When to send digest email, in crontab format (https://en.wikipedia.org/wiki/Cron)
- # 0 0 * * 0 - once a week at midnight on Sunday morning
schedule: "0 0 * * 0",
- # Minimum interval between digest emails to one user
interval: 7,
- # Minimum user inactivity threshold
inactivity_threshold: 7
}
diff --git a/docs/config.md b/docs/config.md
index 5a97033b2..69d389382 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -435,6 +435,18 @@ Authentication / authorization settings.
* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`.
* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable.
+## :email_notifications
+
+Email notifications settings.
+
+ - digest - emails of "what you've missed" for users who have been
+ inactive for a while.
+ - active: globally enable or disable digest emails
+ - schedule: When to send digest email, in [crontab format](https://en.wikipedia.org/wiki/Cron).
+ "0 0 * * 0" is the default, meaning "once a week at midnight on Sunday morning"
+ - interval: Minimum interval between digest emails to one user
+ - inactivity_threshold: Minimum user inactivity threshold
+
# OAuth consumer mode
OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.).
From 5cee2fe9fea4f0c98acd49a2a288ecd44bce3d1f Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Wed, 29 May 2019 21:31:27 +0300
Subject: [PATCH 013/202] Replace Application.get_env/2 with
Pleroma.Config.get/1
---
lib/pleroma/digest_email_worker.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index 65013f77e..f7b3d81cd 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -2,7 +2,7 @@ defmodule Pleroma.DigestEmailWorker do
import Ecto.Query
def run do
- config = Application.get_env(:pleroma, :email_notifications)[:digest]
+ config = Pleroma.Config.get([:email_notifications, :digest])
negative_interval = -Map.fetch!(config, :interval)
inactivity_threshold = Map.fetch!(config, :inactivity_threshold)
inactive_users_query = Pleroma.User.list_inactive_users_query(inactivity_threshold)
From 3e1761058711b12fa995f2b43117fb90ca40c9ad Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Tue, 4 Jun 2019 02:48:21 +0300
Subject: [PATCH 014/202] Add task to test emails
---
lib/mix/tasks/pleroma/digest.ex | 34 +++++++++++++++
lib/pleroma/digest_email_worker.ex | 4 +-
lib/pleroma/emails/user_email.ex | 20 +++++++--
.../web/templates/email/digest.html.eex | 4 +-
test/mix/tasks/pleroma.digest_test.exs | 42 +++++++++++++++++++
5 files changed, 96 insertions(+), 8 deletions(-)
create mode 100644 lib/mix/tasks/pleroma/digest.ex
create mode 100644 test/mix/tasks/pleroma.digest_test.exs
diff --git a/lib/mix/tasks/pleroma/digest.ex b/lib/mix/tasks/pleroma/digest.ex
new file mode 100644
index 000000000..7ac3df5c7
--- /dev/null
+++ b/lib/mix/tasks/pleroma/digest.ex
@@ -0,0 +1,34 @@
+defmodule Mix.Tasks.Pleroma.Digest do
+ use Mix.Task
+ alias Mix.Tasks.Pleroma.Common
+
+ @shortdoc "Manages digest emails"
+ @moduledoc """
+ Manages digest emails
+
+ ## Send digest email since given date (user registration date by default)
+ ignoring user activity status.
+
+ ``mix pleroma.digest test ``
+
+ Example: ``mix pleroma.digest test donaldtheduck 2019-05-20``
+ """
+ def run(["test", nickname | opts]) do
+ Common.start_pleroma()
+
+ user = Pleroma.User.get_by_nickname(nickname)
+
+ last_digest_emailed_at =
+ with [date] <- opts,
+ {:ok, datetime} <- Timex.parse(date, "{YYYY}-{0M}-{0D}") do
+ datetime
+ else
+ _ -> user.inserted_at
+ end
+
+ patched_user = %{user | last_digest_emailed_at: last_digest_emailed_at}
+
+ :ok = Pleroma.DigestEmailWorker.run([patched_user])
+ Mix.shell().info("Digest email have been sent to #{nickname} (#{user.email})")
+ end
+end
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index f7b3d81cd..8c28dca18 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -18,9 +18,9 @@ def run do
|> run()
end
- defp run([]), do: :ok
+ def run([]), do: :ok
- defp run([user | users]) do
+ def run([user | users]) do
with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(user) do
Pleroma.Emails.Mailer.deliver_async(email)
end
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index 64f855112..0ad0aed40 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -103,12 +103,24 @@ def digest_email(user) do
new_notifications =
Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
|> Enum.reduce(%{followers: [], mentions: []}, fn
- %{activity: %{data: %{"type" => "Create"}, actor: actor}} = notification, acc ->
- new_mention = %{data: notification, from: Pleroma.User.get_by_ap_id(actor)}
+ %{activity: %{data: %{"type" => "Create"}, actor: actor} = activity} = notification,
+ acc ->
+ new_mention = %{
+ data: notification,
+ object: Pleroma.Object.normalize(activity),
+ from: Pleroma.User.get_by_ap_id(actor)
+ }
+
%{acc | mentions: [new_mention | acc.mentions]}
- %{activity: %{data: %{"type" => "Follow"}, actor: actor}} = notification, acc ->
- new_follower = %{data: notification, from: Pleroma.User.get_by_ap_id(actor)}
+ %{activity: %{data: %{"type" => "Follow"}, actor: actor} = activity} = notification,
+ acc ->
+ new_follower = %{
+ data: notification,
+ object: Pleroma.Object.normalize(activity),
+ from: Pleroma.User.get_by_ap_id(actor)
+ }
+
%{acc | followers: [new_follower | acc.followers]}
_, acc ->
diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex
index 93c9c884f..c9dd699fd 100644
--- a/lib/pleroma/web/templates/email/digest.html.eex
+++ b/lib/pleroma/web/templates/email/digest.html.eex
@@ -2,8 +2,8 @@
New Mentions:
-<%= for %{data: mention, from: from} <- @mentions do %>
- <%= link from.nickname, to: mention.activity.actor %>: <%= raw mention.activity.object.data["content"] %>
+<%= for %{data: mention, object: object, from: from} <- @mentions do %>
+ <%= link from.nickname, to: mention.activity.actor %>: <%= raw object.data["content"] %>
<% end %>
diff --git a/test/mix/tasks/pleroma.digest_test.exs b/test/mix/tasks/pleroma.digest_test.exs
new file mode 100644
index 000000000..1a54ac35b
--- /dev/null
+++ b/test/mix/tasks/pleroma.digest_test.exs
@@ -0,0 +1,42 @@
+defmodule Mix.Tasks.Pleroma.DigestTest do
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+ import Swoosh.TestAssertions
+
+ alias Pleroma.Web.CommonAPI
+
+ setup_all do
+ Mix.shell(Mix.Shell.Process)
+
+ on_exit(fn ->
+ Mix.shell(Mix.Shell.IO)
+ end)
+
+ :ok
+ end
+
+ describe "pleroma.digest test" do
+ test "Sends digest to the given user" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+
+ Enum.each(0..10, fn i ->
+ {:ok, _activity} =
+ CommonAPI.post(user1, %{
+ "status" => "hey ##{i} @#{user2.nickname}!"
+ })
+ end)
+
+ Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname])
+
+ assert_email_sent(
+ to: {user2.name, user2.email},
+ html_body: ~r/new mentions:/i
+ )
+
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Digest email have been sent"
+ end
+ end
+end
From bd325132ca337c57f63b6443ae44748d9a422f65 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Tue, 4 Jun 2019 03:07:49 +0300
Subject: [PATCH 015/202] Fix tests
---
config/test.exs | 2 ++
test/mix/tasks/pleroma.digest_test.exs | 6 +++---
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/config/test.exs b/config/test.exs
index 41cddb9bd..adb874223 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -67,6 +67,8 @@
config :pleroma, :database, rum_enabled: rum_enabled
IO.puts("RUM enabled: #{rum_enabled}")
+config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp35v0RK9SO8WTPr6QZ"
+
try do
import_config "test.secret.exs"
rescue
diff --git a/test/mix/tasks/pleroma.digest_test.exs b/test/mix/tasks/pleroma.digest_test.exs
index 1a54ac35b..3dafe05fe 100644
--- a/test/mix/tasks/pleroma.digest_test.exs
+++ b/test/mix/tasks/pleroma.digest_test.exs
@@ -30,13 +30,13 @@ test "Sends digest to the given user" do
Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname])
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Digest email have been sent"
+
assert_email_sent(
to: {user2.name, user2.email},
html_body: ~r/new mentions:/i
)
-
- assert_received {:mix_shell, :info, [message]}
- assert message =~ "Digest email have been sent"
end
end
end
From 7718a215e9b20471169bf2474771a4fe486a3050 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Tue, 4 Jun 2019 03:08:00 +0300
Subject: [PATCH 016/202] Update changelog
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2144bbe28..fef10463a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [unreleased]
### Added
+- Digest email for inactive users
- Optional SSH access mode. (Needs `erlang-ssh` package on some distributions).
- [MongooseIM](https://github.com/esl/MongooseIM) http authentication support.
- LDAP authentication
@@ -25,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `notify_email` option
- Configuration: Media proxy `whitelist` option
- Configuration: `report_uri` option
+- Configuration: `email_notifications` option
- Pleroma API: User subscriptions
- Pleroma API: Healthcheck endpoint
- Pleroma API: `/api/v1/pleroma/mascot` per-user frontend mascot configuration endpoints
From f6036ce3b9649902ce1c2af819616ad25f0caef1 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Tue, 4 Jun 2019 03:38:53 +0300
Subject: [PATCH 017/202] Fix tests
---
test/mix/tasks/pleroma.digest_test.exs | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/test/mix/tasks/pleroma.digest_test.exs b/test/mix/tasks/pleroma.digest_test.exs
index 3dafe05fe..595f64ed7 100644
--- a/test/mix/tasks/pleroma.digest_test.exs
+++ b/test/mix/tasks/pleroma.digest_test.exs
@@ -28,9 +28,18 @@ test "Sends digest to the given user" do
})
end)
- Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname])
+ yesterday =
+ NaiveDateTime.add(
+ NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
+ -60 * 60 * 24,
+ :second
+ )
- assert_received {:mix_shell, :info, [message]}
+ {:ok, yesterday_date} = Timex.format(yesterday, "%F", :strftime)
+
+ :ok = Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname, yesterday_date])
+
+ assert_receive {:mix_shell, :info, [message]}
assert message =~ "Digest email have been sent"
assert_email_sent(
From c0fa0001476a8a45878a0c75125627164497eddf Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Fri, 7 Jun 2019 01:22:35 +0300
Subject: [PATCH 018/202] Set default config for digest to false
---
config/config.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/config.exs b/config/config.exs
index 5a05ee043..509f4b081 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -494,7 +494,7 @@
config :pleroma, :email_notifications,
digest: %{
- active: true,
+ active: false,
schedule: "0 0 * * 0",
interval: 7,
inactivity_threshold: 7
From 0384459ce552c50edb582413808a099086b6495e Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Fri, 12 Jul 2019 18:16:54 +0300
Subject: [PATCH 019/202] Update mix.lock
---
mix.lock | 3 +++
1 file changed, 3 insertions(+)
diff --git a/mix.lock b/mix.lock
index 654972ddd..a09022acb 100644
--- a/mix.lock
+++ b/mix.lock
@@ -35,6 +35,8 @@
"excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
+ "gen_stage": {:hex, :gen_stage, "0.14.2", "6a2a578a510c5bfca8a45e6b27552f613b41cf584b58210f017088d3d17d0b14", [:mix], [], "hexpm"},
+ "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
@@ -83,6 +85,7 @@
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
+ "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"},
"swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
From e8fa477793e1395664f79d572800f11994cdd38d Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sat, 13 Jul 2019 19:17:57 +0300
Subject: [PATCH 020/202] Refactor Follows/Followers counter syncronization
- Actually sync counters in the database instead of info cache (which got
overriden after user update was finished anyway)
- Add following count field to user info
- Set hide_followers/hide_follows for remote users based on http status
codes for the first collection page
---
config/test.exs | 3 +-
lib/pleroma/object/fetcher.ex | 6 ++-
lib/pleroma/user.ex | 4 +-
lib/pleroma/user/info.ex | 13 ++++-
lib/pleroma/web/activity_pub/activity_pub.ex | 53 ++++++++++++++++++-
.../web/activity_pub/transmogrifier.ex | 27 ----------
test/web/activity_pub/transmogrifier_test.exs | 28 ----------
7 files changed, 73 insertions(+), 61 deletions(-)
diff --git a/config/test.exs b/config/test.exs
index 96ecf3592..28eea3b00 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -29,7 +29,8 @@
email: "admin@example.com",
notify_email: "noreply@example.com",
skip_thread_containment: false,
- federating: false
+ federating: false,
+ external_user_synchronization: false
# Configure your database
config :pleroma, Pleroma.Repo,
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index 101c21f96..bc3e7e5bc 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -76,7 +76,7 @@ def fetch_object_from_id!(id, options \\ []) do
end
end
- def fetch_and_contain_remote_object_from_id(id) do
+ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
Logger.info("Fetching object #{id} via AP")
with true <- String.starts_with?(id, "http"),
@@ -96,4 +96,8 @@ def fetch_and_contain_remote_object_from_id(id) do
{:error, e}
end
end
+
+ def fetch_and_contain_remote_object_from_id(_id) do
+ {:error, "id must be a string"}
+ end
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index e5a6c2529..c252e8bff 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -114,7 +114,9 @@ def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
def user_info(%User{} = user, args \\ %{}) do
following_count =
- if args[:following_count], do: args[:following_count], else: following_count(user)
+ if args[:following_count],
+ do: args[:following_count],
+ else: user.info.following_count || following_count(user)
follower_count =
if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 08e43ff0f..2d8395b73 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -16,6 +16,7 @@ defmodule Pleroma.User.Info do
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
+ field(:following_count, :integer, default: nil)
field(:locked, :boolean, default: false)
field(:confirmation_pending, :boolean, default: false)
field(:confirmation_token, :string, default: nil)
@@ -195,7 +196,11 @@ def remote_user_creation(info, params) do
:uri,
:hub,
:topic,
- :salmon
+ :salmon,
+ :hide_followers,
+ :hide_follows,
+ :follower_count,
+ :following_count
])
end
@@ -206,7 +211,11 @@ def user_upgrade(info, params) do
:source_data,
:banner,
:locked,
- :magic_key
+ :magic_key,
+ :follower_count,
+ :following_count,
+ :hide_follows,
+ :hide_followers
])
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index a3174a787..0a22fe223 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1013,6 +1013,56 @@ defp object_to_user_data(data) do
{:ok, user_data}
end
+ defp maybe_update_follow_information(data) do
+ with {:enabled, true} <-
+ {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
+ {:ok, following_data} <-
+ Fetcher.fetch_and_contain_remote_object_from_id(data.following_address),
+ following_count <- following_data["totalItems"],
+ hide_follows <- collection_private?(following_data),
+ {:ok, followers_data} <-
+ Fetcher.fetch_and_contain_remote_object_from_id(data.follower_address),
+ followers_count <- followers_data["totalItems"],
+ hide_followers <- collection_private?(followers_data) do
+ info = %{
+ "hide_follows" => hide_follows,
+ "follower_count" => followers_count,
+ "following_count" => following_count,
+ "hide_followers" => hide_followers
+ }
+
+ info = Map.merge(data.info, info)
+ Map.put(data, :info, info)
+ else
+ {:enabled, false} ->
+ data
+
+ e ->
+ Logger.error(
+ "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
+ )
+
+ data
+ end
+ end
+
+ defp collection_private?(data) do
+ if is_map(data["first"]) and
+ data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
+ false
+ else
+ with {:ok, _data} <- Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
+ false
+ else
+ {:error, {:ok, %{status: code}}} when code in [401, 403] ->
+ true
+
+ _e ->
+ false
+ end
+ end
+ end
+
def user_data_from_user_object(data) do
with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data) do
@@ -1024,7 +1074,8 @@ def user_data_from_user_object(data) do
def fetch_and_prepare_user_from_ap_id(ap_id) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
- {:ok, data} <- user_data_from_user_object(data) do
+ {:ok, data} <- user_data_from_user_object(data),
+ data <- maybe_update_follow_information(data) do
{:ok, data}
else
e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index d14490bb5..e34fe6611 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -1087,10 +1087,6 @@ def upgrade_user_from_ap_id(ap_id) do
PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])
end
- if Pleroma.Config.get([:instance, :external_user_synchronization]) do
- update_following_followers_counters(user)
- end
-
{:ok, user}
else
%User{} = user -> {:ok, user}
@@ -1123,27 +1119,4 @@ def maybe_fix_user_object(data) do
data
|> maybe_fix_user_url
end
-
- def update_following_followers_counters(user) do
- info = %{}
-
- following = fetch_counter(user.following_address)
- info = if following, do: Map.put(info, :following_count, following), else: info
-
- followers = fetch_counter(user.follower_address)
- info = if followers, do: Map.put(info, :follower_count, followers), else: info
-
- User.set_info_cache(user, info)
- end
-
- defp fetch_counter(url) do
- with {:ok, %{body: body, status: code}} when code in 200..299 <-
- Pleroma.HTTP.get(
- url,
- [{:Accept, "application/activity+json"}]
- ),
- {:ok, data} <- Jason.decode(body) do
- data["totalItems"]
- end
- end
end
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index b896a532b..6d05138fb 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -1359,32 +1359,4 @@ test "removes recipient's follower collection from cc", %{user: user} do
refute recipient.follower_address in fixed_object["to"]
end
end
-
- test "update_following_followers_counters/1" do
- user1 =
- insert(:user,
- local: false,
- follower_address: "http://localhost:4001/users/masto_closed/followers",
- following_address: "http://localhost:4001/users/masto_closed/following"
- )
-
- user2 =
- insert(:user,
- local: false,
- follower_address: "http://localhost:4001/users/fuser2/followers",
- following_address: "http://localhost:4001/users/fuser2/following"
- )
-
- Transmogrifier.update_following_followers_counters(user1)
- Transmogrifier.update_following_followers_counters(user2)
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
- assert followers == 437
- assert following == 152
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
-
- assert followers == 527
- assert following == 267
- end
end
From e5b850a99115859ceb028c3891f59d5e6ffd5d56 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sat, 13 Jul 2019 23:56:10 +0300
Subject: [PATCH 021/202] Refactor fetching follow information to a separate
function
---
lib/pleroma/user/info.ex | 1 +
lib/pleroma/web/activity_pub/activity_pub.ex | 51 +++++++++++++-------
2 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 2d8395b73..67e8801ea 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -16,6 +16,7 @@ defmodule Pleroma.User.Info do
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
+ # Should be filled in only for remote users
field(:following_count, :integer, default: nil)
field(:locked, :boolean, default: false)
field(:confirmation_pending, :boolean, default: false)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 0a22fe223..eadd335ca 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1013,17 +1013,15 @@ defp object_to_user_data(data) do
{:ok, user_data}
end
- defp maybe_update_follow_information(data) do
- with {:enabled, true} <-
- {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
- {:ok, following_data} <-
- Fetcher.fetch_and_contain_remote_object_from_id(data.following_address),
- following_count <- following_data["totalItems"],
- hide_follows <- collection_private?(following_data),
+ def fetch_follow_information_for_user(user) do
+ with {:ok, following_data} <-
+ Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
+ following_count when is_integer(following_count) <- following_data["totalItems"],
+ {:ok, hide_follows} <- collection_private(following_data),
{:ok, followers_data} <-
- Fetcher.fetch_and_contain_remote_object_from_id(data.follower_address),
- followers_count <- followers_data["totalItems"],
- hide_followers <- collection_private?(followers_data) do
+ Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
+ followers_count when is_integer(followers_count) <- followers_data["totalItems"],
+ {:ok, hide_followers} <- collection_private(followers_data) do
info = %{
"hide_follows" => hide_follows,
"follower_count" => followers_count,
@@ -1031,8 +1029,22 @@ defp maybe_update_follow_information(data) do
"hide_followers" => hide_followers
}
- info = Map.merge(data.info, info)
- Map.put(data, :info, info)
+ info = Map.merge(user.info, info)
+ {:ok, Map.put(user, :info, info)}
+ else
+ {:error, _} = e ->
+ e
+
+ e ->
+ {:error, e}
+ end
+ end
+
+ defp maybe_update_follow_information(data) do
+ with {:enabled, true} <-
+ {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
+ {:ok, data} <- fetch_follow_information_for_user(data) do
+ data
else
{:enabled, false} ->
data
@@ -1046,19 +1058,22 @@ defp maybe_update_follow_information(data) do
end
end
- defp collection_private?(data) do
+ defp collection_private(data) do
if is_map(data["first"]) and
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
- false
+ {:ok, false}
else
with {:ok, _data} <- Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
- false
+ {:ok, false}
else
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
- true
+ {:ok, true}
- _e ->
- false
+ {:error, _} = e ->
+ e
+
+ e ->
+ {:error, e}
end
end
end
From d06d1b751d44802c5c3701f916ae2ce7d3c3be56 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sun, 14 Jul 2019 00:21:35 +0300
Subject: [PATCH 022/202] Use atoms when updating user info
---
lib/pleroma/web/activity_pub/activity_pub.ex | 16 ++++++++--------
lib/pleroma/web/activity_pub/transmogrifier.ex | 6 +++---
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index eadd335ca..df4155d21 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -986,10 +986,10 @@ defp object_to_user_data(data) do
user_data = %{
ap_id: data["id"],
info: %{
- "ap_enabled" => true,
- "source_data" => data,
- "banner" => banner,
- "locked" => locked
+ ap_enabled: true,
+ source_data: data,
+ banner: banner,
+ locked: locked
},
avatar: avatar,
name: data["name"],
@@ -1023,10 +1023,10 @@ def fetch_follow_information_for_user(user) do
followers_count when is_integer(followers_count) <- followers_data["totalItems"],
{:ok, hide_followers} <- collection_private(followers_data) do
info = %{
- "hide_follows" => hide_follows,
- "follower_count" => followers_count,
- "following_count" => following_count,
- "hide_followers" => hide_followers
+ hide_follows: hide_follows,
+ follower_count: followers_count,
+ following_count: following_count,
+ hide_followers: hide_followers
}
info = Map.merge(user.info, info)
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index e34fe6611..10b362908 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -609,13 +609,13 @@ def handle_incoming(
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
- banner = new_user_data[:info]["banner"]
- locked = new_user_data[:info]["locked"] || false
+ banner = new_user_data[:info][:banner]
+ locked = new_user_data[:info][:locked] || false
update_data =
new_user_data
|> Map.take([:name, :bio, :avatar])
- |> Map.put(:info, %{"banner" => banner, "locked" => locked})
+ |> Map.put(:info, %{banner: banner, locked: locked})
actor
|> User.upgrade_changeset(update_data)
From 183da33e005c8a8e8472350a3b6b36ff6f82d67d Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sun, 14 Jul 2019 00:56:02 +0300
Subject: [PATCH 023/202] Add tests for fetch_follow_information_for_user and
check object type when fetching the page
---
lib/pleroma/web/activity_pub/activity_pub.ex | 3 +-
test/web/activity_pub/activity_pub_test.exs | 81 ++++++++++++++++++++
2 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index df4155d21..c821ba45f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1063,7 +1063,8 @@ defp collection_private(data) do
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
{:ok, false}
else
- with {:ok, _data} <- Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
+ with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
+ Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
{:ok, false}
else
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 59d56f3a7..448ffbf54 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -1222,4 +1222,85 @@ test "fetches only public posts for other users" do
assert result.id == activity.id
end
end
+
+ describe "fetch_follow_information_for_user" do
+ test "syncronizes following/followers counters" do
+ user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/fuser2/followers",
+ following_address: "http://localhost:4001/users/fuser2/following"
+ )
+
+ {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
+ assert user.info.follower_count == 527
+ assert user.info.following_count == 267
+ end
+
+ test "detects hidden followers" do
+ mock(fn env ->
+ case env.url do
+ "http://localhost:4001/users/masto_closed/followers?page=1" ->
+ %Tesla.Env{status: 403, body: ""}
+
+ "http://localhost:4001/users/masto_closed/following?page=1" ->
+ %Tesla.Env{
+ status: 200,
+ body:
+ Jason.encode!(%{
+ "id" => "http://localhost:4001/users/masto_closed/following?page=1",
+ "type" => "OrderedCollectionPage"
+ })
+ }
+
+ _ ->
+ apply(HttpRequestMock, :request, [env])
+ end
+ end)
+
+ user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following"
+ )
+
+ {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
+ assert user.info.hide_followers == true
+ assert user.info.hide_follows == false
+ end
+
+ test "detects hidden follows" do
+ mock(fn env ->
+ case env.url do
+ "http://localhost:4001/users/masto_closed/following?page=1" ->
+ %Tesla.Env{status: 403, body: ""}
+
+ "http://localhost:4001/users/masto_closed/followers?page=1" ->
+ %Tesla.Env{
+ status: 200,
+ body:
+ Jason.encode!(%{
+ "id" => "http://localhost:4001/users/masto_closed/followers?page=1",
+ "type" => "OrderedCollectionPage"
+ })
+ }
+
+ _ ->
+ apply(HttpRequestMock, :request, [env])
+ end
+ end)
+
+ user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following"
+ )
+
+ {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
+ assert user.info.hide_followers == false
+ assert user.info.hide_follows == true
+ end
+ end
end
From 0c2dcb4c69ed340d02a4b20a4f341f1d9aaaba38 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sun, 14 Jul 2019 01:58:39 +0300
Subject: [PATCH 024/202] Add follow information refetching after
following/unfollowing
---
lib/pleroma/user.ex | 91 +++++++++++++++-----
lib/pleroma/user/info.ex | 10 +++
lib/pleroma/web/activity_pub/activity_pub.ex | 21 +++--
test/web/activity_pub/activity_pub_test.exs | 18 ++--
4 files changed, 98 insertions(+), 42 deletions(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index c252e8bff..2e9b01205 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -406,6 +406,8 @@ def follow(%User{} = follower, %User{info: info} = followed) do
{1, [follower]} = Repo.update_all(q, [])
+ follower = maybe_update_following_count(follower)
+
{:ok, _} = update_follower_count(followed)
set_cache(follower)
@@ -425,6 +427,8 @@ def unfollow(%User{} = follower, %User{} = followed) do
{1, [follower]} = Repo.update_all(q, [])
+ follower = maybe_update_following_count(follower)
+
{:ok, followed} = update_follower_count(followed)
set_cache(follower)
@@ -698,32 +702,75 @@ def update_note_count(%User{} = user) do
|> update_and_set_cache()
end
- def update_follower_count(%User{} = user) do
- follower_count_query =
- User.Query.build(%{followers: user, deactivated: false})
- |> select([u], %{count: count(u.id)})
+ def maybe_fetch_follow_information(user) do
+ with {:ok, user} <- fetch_follow_information(user) do
+ user
+ else
+ e ->
+ Logger.error(
+ "Follower/Following counter update for #{user.ap_id} failed.\n" <> inspect(e)
+ )
- User
- |> where(id: ^user.id)
- |> join(:inner, [u], s in subquery(follower_count_query))
- |> update([u, s],
- set: [
- info:
- fragment(
- "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
- u.info,
- s.count
- )
- ]
- )
- |> select([u], u)
- |> Repo.update_all([])
- |> case do
- {1, [user]} -> set_cache(user)
- _ -> {:error, user}
+ user
end
end
+ def fetch_follow_information(user) do
+ with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
+ info_cng = User.Info.follow_information_update(user.info, info)
+
+ changeset =
+ user
+ |> change()
+ |> put_embed(:info, info_cng)
+
+ update_and_set_cache(changeset)
+ else
+ {:error, _} = e -> e
+ e -> {:error, e}
+ end
+ end
+
+ def update_follower_count(%User{} = user) do
+ unless user.local == false and Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ follower_count_query =
+ User.Query.build(%{followers: user, deactivated: false})
+ |> select([u], %{count: count(u.id)})
+
+ User
+ |> where(id: ^user.id)
+ |> join(:inner, [u], s in subquery(follower_count_query))
+ |> update([u, s],
+ set: [
+ info:
+ fragment(
+ "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)",
+ u.info,
+ s.count
+ )
+ ]
+ )
+ |> select([u], u)
+ |> Repo.update_all([])
+ |> case do
+ {1, [user]} -> set_cache(user)
+ _ -> {:error, user}
+ end
+ else
+ {:ok, maybe_fetch_follow_information(user)}
+ end
+ end
+
+ def maybe_update_following_count(%User{local: false} = user) do
+ if Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ {:ok, maybe_fetch_follow_information(user)}
+ else
+ user
+ end
+ end
+
+ def maybe_update_following_count(user), do: user
+
def remove_duplicated_following(%User{following: following} = user) do
uniq_following = Enum.uniq(following)
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 67e8801ea..4cc3f2f2c 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -330,4 +330,14 @@ def remove_reblog_mute(info, ap_id) do
cast(info, params, [:muted_reblogs])
end
+
+ def follow_information_update(info, params) do
+ info
+ |> cast(params, [
+ :hide_followers,
+ :hide_follows,
+ :follower_count,
+ :following_count
+ ])
+ end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index c821ba45f..2dd9dbf7f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1022,15 +1022,13 @@ def fetch_follow_information_for_user(user) do
Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
followers_count when is_integer(followers_count) <- followers_data["totalItems"],
{:ok, hide_followers} <- collection_private(followers_data) do
- info = %{
- hide_follows: hide_follows,
- follower_count: followers_count,
- following_count: following_count,
- hide_followers: hide_followers
- }
-
- info = Map.merge(user.info, info)
- {:ok, Map.put(user, :info, info)}
+ {:ok,
+ %{
+ hide_follows: hide_follows,
+ follower_count: followers_count,
+ following_count: following_count,
+ hide_followers: hide_followers
+ }}
else
{:error, _} = e ->
e
@@ -1043,8 +1041,9 @@ def fetch_follow_information_for_user(user) do
defp maybe_update_follow_information(data) do
with {:enabled, true} <-
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
- {:ok, data} <- fetch_follow_information_for_user(data) do
- data
+ {:ok, info} <- fetch_follow_information_for_user(data) do
+ info = Map.merge(data.info, info)
+ Map.put(data, :info, info)
else
{:enabled, false} ->
data
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 448ffbf54..24d8493fe 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -1232,9 +1232,9 @@ test "syncronizes following/followers counters" do
following_address: "http://localhost:4001/users/fuser2/following"
)
- {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
- assert user.info.follower_count == 527
- assert user.info.following_count == 267
+ {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
+ assert info.follower_count == 527
+ assert info.following_count == 267
end
test "detects hidden followers" do
@@ -1265,9 +1265,9 @@ test "detects hidden followers" do
following_address: "http://localhost:4001/users/masto_closed/following"
)
- {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
- assert user.info.hide_followers == true
- assert user.info.hide_follows == false
+ {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
+ assert info.hide_followers == true
+ assert info.hide_follows == false
end
test "detects hidden follows" do
@@ -1298,9 +1298,9 @@ test "detects hidden follows" do
following_address: "http://localhost:4001/users/masto_closed/following"
)
- {:ok, user} = ActivityPub.fetch_follow_information_for_user(user)
- assert user.info.hide_followers == false
- assert user.info.hide_follows == true
+ {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
+ assert info.hide_followers == false
+ assert info.hide_follows == true
end
end
end
From 168dc97c37f274b258b04eb7e883640b84259714 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 14 Jul 2019 22:04:55 +0300
Subject: [PATCH 025/202] Make opts optional in
Pleroma.Notification.for_user_query/2
---
lib/pleroma/notification.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index f680fe049..04bbfa0df 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -33,7 +33,7 @@ def changeset(%Notification{} = notification, attrs) do
|> cast(attrs, [:seen])
end
- def for_user_query(user, opts) do
+ def for_user_query(user, opts \\ []) do
query =
Notification
|> where(user_id: ^user.id)
From b052a9d4d0323eb64c0a741a499906659a674244 Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 14 Jul 2019 22:32:11 +0300
Subject: [PATCH 026/202] Update DigestEmailWorker to compile and send emails
via queue
---
lib/mix/tasks/pleroma/digest.ex | 2 +-
lib/pleroma/digest_email_worker.ex | 15 +++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/lib/mix/tasks/pleroma/digest.ex b/lib/mix/tasks/pleroma/digest.ex
index 19c4ce71e..81c207e10 100644
--- a/lib/mix/tasks/pleroma/digest.ex
+++ b/lib/mix/tasks/pleroma/digest.ex
@@ -27,7 +27,7 @@ def run(["test", nickname | opts]) do
patched_user = %{user | last_digest_emailed_at: last_digest_emailed_at}
- :ok = Pleroma.DigestEmailWorker.run([patched_user])
+ _user = Pleroma.DigestEmailWorker.perform(patched_user)
Mix.shell().info("Digest email have been sent to #{nickname} (#{user.email})")
end
end
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index 8c28dca18..adc24797f 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -1,6 +1,8 @@
defmodule Pleroma.DigestEmailWorker do
import Ecto.Query
+ @queue_name :digest_emails
+
def run do
config = Pleroma.Config.get([:email_notifications, :digest])
negative_interval = -Map.fetch!(config, :interval)
@@ -15,18 +17,19 @@ def run do
select: u
)
|> Pleroma.Repo.all()
- |> run()
+ |> Enum.each(&PleromaJobQueue.enqueue(@queue_name, __MODULE__, [&1]))
end
- def run([]), do: :ok
-
- def run([user | users]) do
+ @doc """
+ Send digest email to the given user.
+ Updates `last_digest_emailed_at` field for the user and returns the updated user.
+ """
+ @spec perform(Pleroma.User.t()) :: Pleroma.User.t()
+ def perform(user) do
with %Swoosh.Email{} = email <- Pleroma.Emails.UserEmail.digest_email(user) do
Pleroma.Emails.Mailer.deliver_async(email)
end
Pleroma.User.touch_last_digest_emailed_at(user)
-
- run(users)
end
end
From e7c175c943e9e3f53df76d812c09cfeffdb1c56b Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Tue, 16 Jul 2019 16:49:50 +0300
Subject: [PATCH 027/202] Use PleromaJobQueue for scheduling
---
lib/pleroma/application.ex | 18 ++++++------------
lib/pleroma/digest_email_worker.ex | 2 +-
lib/pleroma/quantum_scheduler.ex | 4 ----
mix.exs | 4 ++--
mix.lock | 11 ++---------
5 files changed, 11 insertions(+), 28 deletions(-)
delete mode 100644 lib/pleroma/quantum_scheduler.ex
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 29cd14477..7df6bc9ae 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -115,10 +115,6 @@ def start(_type, _args) do
%{
id: Pleroma.ScheduledActivityWorker,
start: {Pleroma.ScheduledActivityWorker, :start_link, []}
- },
- %{
- id: Pleroma.QuantumScheduler,
- start: {Pleroma.QuantumScheduler, :start_link, []}
}
] ++
hackney_pool_children() ++
@@ -231,14 +227,12 @@ defp hackney_pool_children do
defp after_supervisor_start do
with digest_config <- Application.get_env(:pleroma, :email_notifications)[:digest],
- true <- digest_config[:active],
- %Crontab.CronExpression{} = schedule <-
- Crontab.CronExpression.Parser.parse!(digest_config[:schedule]) do
- Pleroma.QuantumScheduler.new_job()
- |> Quantum.Job.set_name(:digest_emails)
- |> Quantum.Job.set_schedule(schedule)
- |> Quantum.Job.set_task(&Pleroma.DigestEmailWorker.run/0)
- |> Pleroma.QuantumScheduler.add_job()
+ true <- digest_config[:active] do
+ PleromaJobQueue.schedule(
+ digest_config[:schedule],
+ :digest_emails,
+ Pleroma.DigestEmailWorker
+ )
end
:ok
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index adc24797f..18e67d39b 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -3,7 +3,7 @@ defmodule Pleroma.DigestEmailWorker do
@queue_name :digest_emails
- def run do
+ def perform do
config = Pleroma.Config.get([:email_notifications, :digest])
negative_interval = -Map.fetch!(config, :interval)
inactivity_threshold = Map.fetch!(config, :inactivity_threshold)
diff --git a/lib/pleroma/quantum_scheduler.ex b/lib/pleroma/quantum_scheduler.ex
deleted file mode 100644
index 9a3df81f6..000000000
--- a/lib/pleroma/quantum_scheduler.ex
+++ /dev/null
@@ -1,4 +0,0 @@
-defmodule Pleroma.QuantumScheduler do
- use Quantum.Scheduler,
- otp_app: :pleroma
-end
diff --git a/mix.exs b/mix.exs
index a4f468726..25332deb9 100644
--- a/mix.exs
+++ b/mix.exs
@@ -140,7 +140,8 @@ defp deps do
{:http_signatures,
git: "https://git.pleroma.social/pleroma/http_signatures.git",
ref: "9789401987096ead65646b52b5a2ca6bf52fc531"},
- {:pleroma_job_queue, "~> 0.2.0"},
+ {:pleroma_job_queue,
+ git: "https://git.pleroma.social/pleroma/pleroma_job_queue.git", ref: "0637ccb1"},
{:telemetry, "~> 0.3"},
{:prometheus_ex, "~> 3.0"},
{:prometheus_plugs, "~> 1.1"},
@@ -148,7 +149,6 @@ defp deps do
{:prometheus_ecto, "~> 1.4"},
{:recon, github: "ferd/recon", tag: "2.4.0"},
{:quack, "~> 0.1.1"},
- {:quantum, "~> 2.3"},
{:joken, "~> 2.0"},
{:benchee, "~> 1.0"},
{:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
diff --git a/mix.lock b/mix.lock
index 4f1214bca..3621d9c27 100644
--- a/mix.lock
+++ b/mix.lock
@@ -15,7 +15,7 @@
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
- "crontab": {:hex, :crontab, "1.1.5", "2c9439506ceb0e9045de75879e994b88d6f0be88bfe017d58cb356c66c4a5482", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
+ "crontab": {:hex, :crontab, "1.1.7", "b9219f0bdc8678b94143655a8f229716c5810c0636a4489f98c0956137e53985", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
@@ -35,8 +35,6 @@
"excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
"gen_smtp": {:hex, :gen_smtp, "0.14.0", "39846a03522456077c6429b4badfd1d55e5e7d0fdfb65e935b7c5e38549d9202", [:rebar3], [], "hexpm"},
- "gen_stage": {:hex, :gen_stage, "0.14.2", "6a2a578a510c5bfca8a45e6b27552f613b41cf584b58210f017088d3d17d0b14", [:mix], [], "hexpm"},
- "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"html_entities": {:hex, :html_entities, "0.4.0", "f2fee876858cf6aaa9db608820a3209e45a087c5177332799592142b50e89a6b", [:mix], [], "hexpm"},
@@ -47,7 +45,6 @@
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"},
"jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
- "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
@@ -66,26 +63,22 @@
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.2.0", "a7e0b32077cd6d2323ae15198839b05d9caddfa20663fd85787479e81f89520e", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 0.1", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm"},
- "pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
+ "pleroma_job_queue": {:git, "https://git.pleroma.social/pleroma/pleroma_job_queue.git", "0637ccb163bab951fc8cd8bcfa3e6c10f0cc0c66", [ref: "0637ccb1"]},
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "2.0.2", "6055f16868cc4882b24b6e1d63d2bada94fb4978413377a3b32ac16c18dffba2", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
- "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.3", "5754dee2fdf6e9e508cbf49ab138df964278700b764177e8f3871e658b345a1e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"prometheus": {:hex, :prometheus, "4.2.2", "a830e77b79dc6d28183f4db050a7cac926a6c58f1872f9ef94a35cd989aceef8", [:mix, :rebar3], [], "hexpm"},
"prometheus_ecto": {:hex, :prometheus_ecto, "1.4.1", "6c768ea9654de871e5b32fab2eac348467b3021604ebebbcbd8bcbe806a65ed5", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_phoenix": {:hex, :prometheus_phoenix, "1.2.1", "964a74dfbc055f781d3a75631e06ce3816a2913976d1df7830283aa3118a797a", [:mix], [{:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm"},
"prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm"},
- "prometheus_process_collector": {:hex, :prometheus_process_collector, "1.4.0", "6dbd39e3165b9ef1c94a7a820e9ffe08479f949dcdd431ed4aaea7b250eebfde", [:rebar3], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm"},
"quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
- "quantum": {:hex, :quantum, "2.3.4", "72a0e8855e2adc101459eac8454787cb74ab4169de6ca50f670e72142d4960e9", [:mix], [{:calendar, "~> 0.17", [hex: :calendar, repo: "hexpm", optional: true]}, {:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.12", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:swarm, "~> 3.3", [hex: :swarm, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: true]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"recon": {:git, "https://github.com/ferd/recon.git", "75d70c7c08926d2f24f1ee6de14ee50fe8a52763", [tag: "2.4.0"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
- "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm"},
"swoosh": {:hex, :swoosh, "0.23.2", "7dda95ff0bf54a2298328d6899c74dae1223777b43563ccebebb4b5d2b61df38", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
From ae4fc58589ac48a0853719e6f83b2559b6de44fb Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sat, 20 Jul 2019 01:24:01 +0300
Subject: [PATCH 028/202] Remove flavour from userinfo
---
lib/pleroma/user/info.ex | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index ae2f66cf1..60b7a82ab 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -43,7 +43,6 @@ defmodule Pleroma.User.Info do
field(:hide_follows, :boolean, default: false)
field(:hide_favorites, :boolean, default: true)
field(:pinned_activities, {:array, :string}, default: [])
- field(:flavour, :string, default: nil)
field(:email_notifications, :map, default: %{"digest" => false})
field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: [])
From d4ee76ab6355db0bed59b5126fe04d3399561798 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sat, 20 Jul 2019 18:52:41 +0000
Subject: [PATCH 029/202] Apply suggestion to lib/pleroma/user.ex
---
lib/pleroma/user.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 2e9b01205..956ec6240 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -708,7 +708,7 @@ def maybe_fetch_follow_information(user) do
else
e ->
Logger.error(
- "Follower/Following counter update for #{user.ap_id} failed.\n" <> inspect(e)
+ "Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}"
)
user
From c3ecaea64dd377b586e3b2a5316e90884ec78fe6 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Sat, 20 Jul 2019 18:53:00 +0000
Subject: [PATCH 030/202] Apply suggestion to lib/pleroma/object/fetcher.ex
---
lib/pleroma/object/fetcher.ex | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index bc3e7e5bc..1e60d0082 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -97,7 +97,8 @@ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
end
end
- def fetch_and_contain_remote_object_from_id(_id) do
+ def fetch_and_contain_remote_object_from_id(%{"id" => id), do: fetch_and_contain_remote_object_from_id(id)
+ def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"}
{:error, "id must be a string"}
end
end
From afc7708dbe00a70be616f00f01b22b0d01b9b61b Mon Sep 17 00:00:00 2001
From: Roman Chvanikov
Date: Sun, 21 Jul 2019 00:01:58 +0300
Subject: [PATCH 031/202] Fix pleroma_job_queue version
---
mix.exs | 3 +--
mix.lock | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/mix.exs b/mix.exs
index 0eb92bb12..315ac808d 100644
--- a/mix.exs
+++ b/mix.exs
@@ -140,8 +140,7 @@ defp deps do
{:http_signatures,
git: "https://git.pleroma.social/pleroma/http_signatures.git",
ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"},
- {:pleroma_job_queue,
- git: "https://git.pleroma.social/pleroma/pleroma_job_queue.git", ref: "0637ccb1"},
+ {:pleroma_job_queue, "~> 0.3"},
{:telemetry, "~> 0.3"},
{:prometheus_ex, "~> 3.0"},
{:prometheus_plugs, "~> 1.1"},
diff --git a/mix.lock b/mix.lock
index d14dcac21..e7c2f25fc 100644
--- a/mix.lock
+++ b/mix.lock
@@ -63,7 +63,7 @@
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "0.2.0", "a7e0b32077cd6d2323ae15198839b05d9caddfa20663fd85787479e81f89520e", [:mix], [{:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.2", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 0.1", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm"},
- "pleroma_job_queue": {:git, "https://git.pleroma.social/pleroma/pleroma_job_queue.git", "0637ccb163bab951fc8cd8bcfa3e6c10f0cc0c66", [ref: "0637ccb1"]},
+ "pleroma_job_queue": {:hex, :pleroma_job_queue, "0.3.0", "b84538d621f0c3d6fcc1cff9d5648d3faaf873b8b21b94e6503428a07a48ec47", [:mix], [{:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}], "hexpm"},
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"plug_cowboy": {:hex, :plug_cowboy, "2.0.2", "6055f16868cc4882b24b6e1d63d2bada94fb4978413377a3b32ac16c18dffba2", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
From e818381042b2bd1d6838f61b150d2816115bddb5 Mon Sep 17 00:00:00 2001
From: kPherox
Date: Tue, 23 Jul 2019 18:45:04 +0900
Subject: [PATCH 032/202] Use User.get_or_fetch/1 instead of
OStatus.find_or_make_user/1
---
lib/pleroma/web/twitter_api/controllers/util_controller.ex | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 9e4da7dca..39bc6147c 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -17,7 +17,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.OStatus
alias Pleroma.Web.WebFinger
def help_test(conn, _params) do
@@ -60,7 +59,7 @@ def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
redirect(conn, to: "/notice/#{activity_id}")
else
- {err, followee} = OStatus.find_or_make_user(acct)
+ {err, followee} = User.get_or_fetch(acct)
avatar = User.avatar_url(followee)
name = followee.nickname
id = followee.id
From 4af4f6166bd04b5a302856034fdda94dd61045ed Mon Sep 17 00:00:00 2001
From: Sadposter
Date: Wed, 24 Jul 2019 11:09:06 +0100
Subject: [PATCH 033/202] honour domain blocks on streaming notifications
---
lib/pleroma/web/streamer.ex | 3 +++
test/web/streamer_test.exs | 18 ++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 86e2dc4dd..d233d2a41 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -234,10 +234,13 @@ defp should_send?(%User{} = user, %Activity{} = item) do
blocks = user.info.blocks || []
mutes = user.info.mutes || []
reblog_mutes = user.info.muted_reblogs || []
+ domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
+ %{host: host} = URI.parse(parent.data["actor"])
with parent when not is_nil(parent) <- Object.normalize(item),
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
+ false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host),
true <- thread_containment(item, user) do
true
else
diff --git a/test/web/streamer_test.exs b/test/web/streamer_test.exs
index 8f56e7486..95d5e5d58 100644
--- a/test/web/streamer_test.exs
+++ b/test/web/streamer_test.exs
@@ -103,6 +103,24 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is
Streamer.stream("user:notification", notif)
Task.await(task)
end
+
+ test "it doesn't send notify to the 'user:notification' stream' when a domain is blocked", %{
+ user: user
+ } do
+ user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
+ task = Task.async(fn -> refute_receive {:text, _}, 4_000 end)
+
+ Streamer.add_socket(
+ "user:notification",
+ %{transport_pid: task.pid, assigns: %{user: user}}
+ )
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+ {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
+ {:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
+ Streamer.stream("user:notification", notif)
+ Task.await(task)
+ end
end
test "it sends to public" do
From 48bd3be9cb9b378dfde78e769e2f00ed77129ab9 Mon Sep 17 00:00:00 2001
From: Sadposter
Date: Wed, 24 Jul 2019 11:11:33 +0100
Subject: [PATCH 034/202] move domain block check to with block
---
lib/pleroma/web/streamer.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index d233d2a41..e4259e869 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -235,11 +235,11 @@ defp should_send?(%User{} = user, %Activity{} = item) do
mutes = user.info.mutes || []
reblog_mutes = user.info.muted_reblogs || []
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
- %{host: host} = URI.parse(parent.data["actor"])
with parent when not is_nil(parent) <- Object.normalize(item),
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
+ %{host: host} <- URI.parse(parent.data["actor"]),
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host),
true <- thread_containment(item, user) do
true
From f5d574f4ed9aa997a47d03f02adeb701d96f6789 Mon Sep 17 00:00:00 2001
From: sadposter
Date: Wed, 24 Jul 2019 11:35:16 +0100
Subject: [PATCH 035/202] check both item and parent domain blocks
---
lib/pleroma/web/streamer.ex | 6 ++++--
test/web/streamer_test.exs | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index e4259e869..9ee331030 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -239,8 +239,10 @@ defp should_send?(%User{} = user, %Activity{} = item) do
with parent when not is_nil(parent) <- Object.normalize(item),
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
- %{host: host} <- URI.parse(parent.data["actor"]),
- false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host),
+ %{host: item_host} <- URI.parse(item.actor),
+ %{host: parent_host} <- URI.parse(parent.data["actor"]),
+ false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
+ false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, parent_host),
true <- thread_containment(item, user) do
true
else
diff --git a/test/web/streamer_test.exs b/test/web/streamer_test.exs
index 95d5e5d58..d47b37efb 100644
--- a/test/web/streamer_test.exs
+++ b/test/web/streamer_test.exs
@@ -115,9 +115,10 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is
%{transport_pid: task.pid, assigns: %{user: user}}
)
- {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
+
Streamer.stream("user:notification", notif)
Task.await(task)
end
From 4504135894d5b52c74818fadc3f7ed49ace1702b Mon Sep 17 00:00:00 2001
From: Eugenij
Date: Wed, 24 Jul 2019 15:12:27 +0000
Subject: [PATCH 036/202] Add `domain_blocking` to the relationship API (GET
/api/v1/accounts/relationships)
---
CHANGELOG.md | 1 +
lib/pleroma/user.ex | 25 ++++++++++++-------
.../web/mastodon_api/views/account_view.ex | 6 ++---
test/web/mastodon_api/account_view_test.exs | 10 ++++++++
4 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35a5a6c21..a3f54d19e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Add support for categories for custom emojis by reusing the group feature.
- Mastodon API: Add support for muting/unmuting notifications
- Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`).
+- Mastodon API: Add support for the `domain_blocking` attribute in the relationship API (`GET /api/v1/accounts/relationships`).
- Mastodon API: Add `pleroma.deactivated` to the Account entity
- Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
- Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 982ca8bc1..974f6df18 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -882,19 +882,26 @@ def muted_notifications?(nil, _), do: false
def muted_notifications?(user, %{ap_id: ap_id}),
do: Enum.member?(user.info.muted_notifications, ap_id)
- def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
- blocks = info.blocks
-
- domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(info.domain_blocks)
-
- %{host: host} = URI.parse(ap_id)
-
- Enum.member?(blocks, ap_id) ||
- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
+ def blocks?(%User{} = user, %User{} = target) do
+ blocks_ap_id?(user, target) || blocks_domain?(user, target)
end
def blocks?(nil, _), do: false
+ def blocks_ap_id?(%User{} = user, %User{} = target) do
+ Enum.member?(user.info.blocks, target.ap_id)
+ end
+
+ def blocks_ap_id?(_, _), do: false
+
+ def blocks_domain?(%User{} = user, %User{} = target) do
+ domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks)
+ %{host: host} = URI.parse(target.ap_id)
+ Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
+ end
+
+ def blocks_domain?(_, _), do: false
+
def subscribed_to?(user, %{ap_id: ap_id}) do
with %User{} = target <- get_cached_by_ap_id(ap_id) do
Enum.member?(target.info.subscribers, user.ap_id)
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index befb35c26..b2b06eeb9 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -50,13 +50,13 @@ def render("relationship.json", %{user: %User{} = user, target: %User{} = target
id: to_string(target.id),
following: User.following?(user, target),
followed_by: User.following?(target, user),
- blocking: User.blocks?(user, target),
- blocked_by: User.blocks?(target, user),
+ blocking: User.blocks_ap_id?(user, target),
+ blocked_by: User.blocks_ap_id?(target, user),
muting: User.mutes?(user, target),
muting_notifications: User.muted_notifications?(user, target),
subscribing: User.subscribed_to?(user, target),
requested: requested,
- domain_blocking: false,
+ domain_blocking: User.blocks_domain?(user, target),
showing_reblogs: User.showing_reblogs?(user, target),
endorsed: false
}
diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs
index fa44d35cc..905e9af98 100644
--- a/test/web/mastodon_api/account_view_test.exs
+++ b/test/web/mastodon_api/account_view_test.exs
@@ -231,6 +231,16 @@ test "represent a relationship for the blocking and blocked user" do
AccountView.render("relationship.json", %{user: user, target: other_user})
end
+ test "represent a relationship for the user blocking a domain" do
+ user = insert(:user)
+ other_user = insert(:user, ap_id: "https://bad.site/users/other_user")
+
+ {:ok, user} = User.block_domain(user, "bad.site")
+
+ assert %{domain_blocking: true, blocking: false} =
+ AccountView.render("relationship.json", %{user: user, target: other_user})
+ end
+
test "represent a relationship for the user with a pending follow request" do
user = insert(:user)
other_user = insert(:user, %{info: %User.Info{locked: true}})
From 55341ac71740d3d8aded9c6520e06b9c509a6670 Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 24 Jul 2019 15:13:10 +0000
Subject: [PATCH 037/202] tests WebFinger
---
lib/pleroma/plugs/set_format_plug.ex | 24 +++++++++++
lib/pleroma/web/web_finger/web_finger.ex | 3 +-
.../web/web_finger/web_finger_controller.ex | 43 +++++++++----------
test/plugs/set_format_plug_test.exs | 38 ++++++++++++++++
test/support/http_request_mock.ex | 9 ++++
.../web_finger/web_finger_controller_test.exs | 43 +++++++++++++++++++
test/web/web_finger/web_finger_test.exs | 5 +++
7 files changed, 141 insertions(+), 24 deletions(-)
create mode 100644 lib/pleroma/plugs/set_format_plug.ex
create mode 100644 test/plugs/set_format_plug_test.exs
diff --git a/lib/pleroma/plugs/set_format_plug.ex b/lib/pleroma/plugs/set_format_plug.ex
new file mode 100644
index 000000000..5ca741c64
--- /dev/null
+++ b/lib/pleroma/plugs/set_format_plug.ex
@@ -0,0 +1,24 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.SetFormatPlug do
+ import Plug.Conn, only: [assign: 3, fetch_query_params: 1]
+
+ def init(_), do: nil
+
+ def call(conn, _) do
+ case get_format(conn) do
+ nil -> conn
+ format -> assign(conn, :format, format)
+ end
+ end
+
+ defp get_format(conn) do
+ conn.private[:phoenix_format] ||
+ case fetch_query_params(conn) do
+ %{query_params: %{"_format" => format}} -> format
+ _ -> nil
+ end
+ end
+end
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index fa34c7ced..0514ade2b 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -187,6 +187,7 @@ def find_lrdd_template(domain) do
end
end
+ @spec finger(String.t()) :: {:ok, map()} | {:error, any()}
def finger(account) do
account = String.trim_leading(account, "@")
@@ -220,8 +221,6 @@ def finger(account) do
else
with {:ok, doc} <- Jason.decode(body) do
webfinger_from_json(doc)
- else
- {:error, e} -> e
end
end
else
diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex
index b77c75ec5..896eb15f9 100644
--- a/lib/pleroma/web/web_finger/web_finger_controller.ex
+++ b/lib/pleroma/web/web_finger/web_finger_controller.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do
alias Pleroma.Web.WebFinger
+ plug(Pleroma.Plugs.SetFormatPlug)
plug(Pleroma.Web.FederatingPlug)
def host_meta(conn, _params) do
@@ -17,30 +18,28 @@ def host_meta(conn, _params) do
|> send_resp(200, xml)
end
- def webfinger(conn, %{"resource" => resource}) do
- case get_format(conn) do
- n when n in ["xml", "xrd+xml"] ->
- with {:ok, response} <- WebFinger.webfinger(resource, "XML") do
- conn
- |> put_resp_content_type("application/xrd+xml")
- |> send_resp(200, response)
- else
- _e -> send_resp(conn, 404, "Couldn't find user")
- end
-
- n when n in ["json", "jrd+json"] ->
- with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do
- json(conn, response)
- else
- _e -> send_resp(conn, 404, "Couldn't find user")
- end
-
- _ ->
- send_resp(conn, 404, "Unsupported format")
+ def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource})
+ when format in ["xml", "xrd+xml"] do
+ with {:ok, response} <- WebFinger.webfinger(resource, "XML") do
+ conn
+ |> put_resp_content_type("application/xrd+xml")
+ |> send_resp(200, response)
+ else
+ _e -> send_resp(conn, 404, "Couldn't find user")
end
end
- def webfinger(conn, _params) do
- send_resp(conn, 400, "Bad Request")
+ def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource})
+ when format in ["json", "jrd+json"] do
+ with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do
+ json(conn, response)
+ else
+ _e ->
+ conn
+ |> put_status(404)
+ |> json("Couldn't find user")
+ end
end
+
+ def webfinger(conn, _params), do: send_resp(conn, 400, "Bad Request")
end
diff --git a/test/plugs/set_format_plug_test.exs b/test/plugs/set_format_plug_test.exs
new file mode 100644
index 000000000..bb21956bb
--- /dev/null
+++ b/test/plugs/set_format_plug_test.exs
@@ -0,0 +1,38 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.SetFormatPlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.SetFormatPlug
+
+ test "set format from params" do
+ conn =
+ :get
+ |> conn("/cofe?_format=json")
+ |> SetFormatPlug.call([])
+
+ assert %{format: "json"} == conn.assigns
+ end
+
+ test "set format from header" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> put_private(:phoenix_format, "xml")
+ |> SetFormatPlug.call([])
+
+ assert %{format: "xml"} == conn.assigns
+ end
+
+ test "doesn't set format" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> SetFormatPlug.call([])
+
+ refute conn.assigns[:format]
+ end
+end
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 7811f7807..31c085e0d 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -614,6 +614,15 @@ def get(
}}
end
+ def get(
+ "https://social.heldscal.la/.well-known/webfinger?resource=invalid_content@social.heldscal.la",
+ _,
+ _,
+ Accept: "application/xrd+xml,application/jrd+json"
+ ) do
+ {:ok, %Tesla.Env{status: 200, body: ""}}
+ end
+
def get("http://framatube.org/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/web/web_finger/web_finger_controller_test.exs
index a14ed3126..7d861cbf5 100644
--- a/test/web/web_finger/web_finger_controller_test.exs
+++ b/test/web/web_finger/web_finger_controller_test.exs
@@ -19,6 +19,19 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
:ok
end
+ test "GET host-meta" do
+ response =
+ build_conn()
+ |> get("/.well-known/host-meta")
+
+ assert response.status == 200
+
+ assert response.resp_body ==
+ ~s( )
+ end
+
test "Webfinger JRD" do
user = insert(:user)
@@ -30,6 +43,16 @@ test "Webfinger JRD" do
assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost"
end
+ test "it returns 404 when user isn't found (JSON)" do
+ result =
+ build_conn()
+ |> put_req_header("accept", "application/jrd+json")
+ |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
+ |> json_response(404)
+
+ assert result == "Couldn't find user"
+ end
+
test "Webfinger XML" do
user = insert(:user)
@@ -41,6 +64,26 @@ test "Webfinger XML" do
assert response(response, 200)
end
+ test "it returns 404 when user isn't found (XML)" do
+ result =
+ build_conn()
+ |> put_req_header("accept", "application/xrd+xml")
+ |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
+ |> response(404)
+
+ assert result == "Couldn't find user"
+ end
+
+ test "Sends a 404 when invalid format" do
+ user = insert(:user)
+
+ assert_raise Phoenix.NotAcceptableError, fn ->
+ build_conn()
+ |> put_req_header("accept", "text/html")
+ |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
+ end
+ end
+
test "Sends a 400 when resource param is missing" do
response =
build_conn()
diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs
index 0578b4b8e..abf512604 100644
--- a/test/web/web_finger/web_finger_test.exs
+++ b/test/web/web_finger/web_finger_test.exs
@@ -40,6 +40,11 @@ test "works for ap_ids" do
end
describe "fingering" do
+ test "returns error when fails parse xml or json" do
+ user = "invalid_content@social.heldscal.la"
+ assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
+ end
+
test "returns the info for an OStatus user" do
user = "shp@social.heldscal.la"
From ac27b94ffa49c15850eab591fc1e0e729ddb4167 Mon Sep 17 00:00:00 2001
From: kPherox
Date: Wed, 24 Jul 2019 23:38:38 +0900
Subject: [PATCH 038/202] Change to not require `magic-public-key` on WebFinger
---
lib/pleroma/web/web_finger/web_finger.ex | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex
index fa34c7ced..ad3884c0e 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -86,11 +86,17 @@ def represent_user(user, "XML") do
|> XmlBuilder.to_doc()
end
- defp get_magic_key(magic_key) do
- "data:application/magic-public-key," <> magic_key = magic_key
+ defp get_magic_key("data:application/magic-public-key," <> magic_key) do
{:ok, magic_key}
- rescue
- MatchError -> {:error, "Missing magic key data."}
+ end
+
+ defp get_magic_key(nil) do
+ Logger.debug("Undefined magic key.")
+ {:ok, nil}
+ end
+
+ defp get_magic_key(_) do
+ {:error, "Missing magic key data."}
end
defp webfinger_from_xml(doc) do
From 84fca14c3c6b3a5a6f3b0894903867dfa50a78bb Mon Sep 17 00:00:00 2001
From: feld
Date: Wed, 24 Jul 2019 15:35:25 +0000
Subject: [PATCH 039/202] Do not prepend /media/ when using base_url
This ensures admin has full control over the path where media resides.
---
lib/pleroma/upload.ex | 9 ++++++++-
test/upload_test.exs | 46 ++++++++++++++++++++++++++-----------------
2 files changed, 36 insertions(+), 19 deletions(-)
diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex
index c47d65241..9f0adde5b 100644
--- a/lib/pleroma/upload.ex
+++ b/lib/pleroma/upload.ex
@@ -228,7 +228,14 @@ defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do
""
end
- [base_url, "media", path]
+ prefix =
+ if is_nil(Pleroma.Config.get([__MODULE__, :base_url])) do
+ "media"
+ else
+ ""
+ end
+
+ [base_url, prefix, path]
|> Path.join()
end
diff --git a/test/upload_test.exs b/test/upload_test.exs
index 32c6977d1..95b16078b 100644
--- a/test/upload_test.exs
+++ b/test/upload_test.exs
@@ -122,24 +122,6 @@ test "returns a media url" do
assert String.starts_with?(url, Pleroma.Web.base_url() <> "/media/")
end
- test "returns a media url with configured base_url" do
- base_url = "https://cache.pleroma.social"
-
- File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
-
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image_tmp.jpg"),
- filename: "image.jpg"
- }
-
- {:ok, data} = Upload.store(file, base_url: base_url)
-
- assert %{"url" => [%{"href" => url}]} = data
-
- assert String.starts_with?(url, base_url <> "/media/")
- end
-
test "copies the file to the configured folder with deduping" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
@@ -266,4 +248,32 @@ test "escapes reserved uri characters" do
"%3A%3F%23%5B%5D%40%21%24%26%5C%27%28%29%2A%2B%2C%3B%3D.jpg"
end
end
+
+ describe "Setting a custom base_url for uploaded media" do
+ setup do
+ Pleroma.Config.put([Pleroma.Upload, :base_url], "https://cache.pleroma.social")
+
+ on_exit(fn ->
+ Pleroma.Config.put([Pleroma.Upload, :base_url], nil)
+ end)
+ end
+
+ test "returns a media url with configured base_url" do
+ base_url = Pleroma.Config.get([Pleroma.Upload, :base_url])
+
+ File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
+
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ filename: "image.jpg"
+ }
+
+ {:ok, data} = Upload.store(file, base_url: base_url)
+
+ assert %{"url" => [%{"href" => url}]} = data
+
+ refute String.starts_with?(url, base_url <> "/media/")
+ end
+ end
end
From 8d9f43e1d1a55f445e4e6e4659b9493cd35d99d9 Mon Sep 17 00:00:00 2001
From: kPherox
Date: Thu, 25 Jul 2019 01:27:34 +0900
Subject: [PATCH 040/202] Add WebFinger test for AP-only account
---
test/fixtures/tesla_mock/kpherox@mstdn.jp.xml | 10 ++++++++++
test/support/http_request_mock.ex | 8 ++++++++
test/web/web_finger/web_finger_test.exs | 14 ++++++++++++++
3 files changed, 32 insertions(+)
create mode 100644 test/fixtures/tesla_mock/kpherox@mstdn.jp.xml
diff --git a/test/fixtures/tesla_mock/kpherox@mstdn.jp.xml b/test/fixtures/tesla_mock/kpherox@mstdn.jp.xml
new file mode 100644
index 000000000..2ec134eaa
--- /dev/null
+++ b/test/fixtures/tesla_mock/kpherox@mstdn.jp.xml
@@ -0,0 +1,10 @@
+
+
+ acct:kPherox@mstdn.jp
+ https://mstdn.jp/@kPherox
+ https://mstdn.jp/users/kPherox
+
+
+
+
+
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 7811f7807..6684a36e7 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -915,6 +915,14 @@ def get("https://info.pleroma.site/activity3.json", _, _, _) do
{:ok, %Tesla.Env{status: 404, body: ""}}
end
+ def get("https://mstdn.jp/.well-known/webfinger?resource=acct:kpherox@mstdn.jp", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml")
+ }}
+ end
+
def get(url, query, body, headers) do
{:error,
"Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs
index 0578b4b8e..0d3ef6717 100644
--- a/test/web/web_finger/web_finger_test.exs
+++ b/test/web/web_finger/web_finger_test.exs
@@ -81,6 +81,20 @@ test "returns the correctly for json ostatus users" do
assert data["subscribe_address"] == "https://gnusocial.de/main/ostatussub?profile={uri}"
end
+ test "it work for AP-only user" do
+ user = "kpherox@mstdn.jp"
+
+ {:ok, data} = WebFinger.finger(user)
+
+ assert data["magic_key"] == nil
+ assert data["salmon"] == nil
+
+ assert data["topic"] == "https://mstdn.jp/users/kPherox.atom"
+ assert data["subject"] == "acct:kPherox@mstdn.jp"
+ assert data["ap_id"] == "https://mstdn.jp/users/kPherox"
+ assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}"
+ end
+
test "it works for friendica" do
user = "lain@squeet.me"
From b20020da160404f6f668eec2a2a42aaa2b6a09b2 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Wed, 24 Jul 2019 19:28:21 +0000
Subject: [PATCH 041/202] Show the url advertised in the Activity in the Status
JSON response
---
.../web/mastodon_api/views/status_view.ex | 2 +-
.../tesla_mock/wedistribute-article.json | 18 +++++++++++
.../tesla_mock/wedistribute-user.json | 31 +++++++++++++++++++
test/object/fetcher_test.exs | 7 +++++
test/support/http_request_mock.ex | 16 ++++++++++
test/web/mastodon_api/status_view_test.exs | 10 ++++++
6 files changed, 83 insertions(+), 1 deletion(-)
create mode 100644 test/fixtures/tesla_mock/wedistribute-article.json
create mode 100644 test/fixtures/tesla_mock/wedistribute-user.json
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index de9425959..80df9b2ac 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -222,7 +222,7 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
if user.local do
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)
else
- object.data["external_url"] || object.data["id"]
+ object.data["url"] || object.data["external_url"] || object.data["id"]
end
%{
diff --git a/test/fixtures/tesla_mock/wedistribute-article.json b/test/fixtures/tesla_mock/wedistribute-article.json
new file mode 100644
index 000000000..39dc1b982
--- /dev/null
+++ b/test/fixtures/tesla_mock/wedistribute-article.json
@@ -0,0 +1,18 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams"
+ ],
+ "type": "Article",
+ "name": "The end is near: Mastodon plans to drop OStatus support",
+ "content": "\nThe days of OStatus are numbered. The venerable protocol has served as a glue between many different types of servers since the early days of the Fediverse, connecting StatusNet (now GNU Social) to Friendica, Hubzilla, Mastodon, and Pleroma.
\n\n\n\nNow that many fediverse platforms support ActivityPub as a successor protocol, Mastodon appears to be drawing a line in the sand. In a Patreon update , Eugen Rochko writes:
\n\n\n\n...OStatus...has overstayed its welcome in the code...and now that most of the network uses ActivityPub, it's time for it to go.
Eugen Rochko, Mastodon creator \n\n\n\nThe pull request to remove Pubsubhubbub and Salmon, two of the main components of OStatus, has already been merged into Mastodon's master branch.
\n\n\n\nSome projects will be left in the dark as a side effect of this. GNU Social and PostActiv, for example, both only communicate using OStatus. While some discussion exists regarding adopting ActivityPub for GNU Social, and a plugin is in development , it hasn't been formally adopted yet. We just hope that the Free Software Foundation's instance gets updated in time!
\n",
+ "summary": "One of the largest platforms in the federated social web is dropping the protocol that it started with.",
+ "attributedTo": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog",
+ "url": "https://wedistribute.org/2019/07/mastodon-drops-ostatus/",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public",
+ "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/followers"
+ ],
+ "id": "https://wedistribute.org/wp-json/pterotype/v1/object/85810",
+ "likes": "https://wedistribute.org/wp-json/pterotype/v1/object/85810/likes",
+ "shares": "https://wedistribute.org/wp-json/pterotype/v1/object/85810/shares"
+}
diff --git a/test/fixtures/tesla_mock/wedistribute-user.json b/test/fixtures/tesla_mock/wedistribute-user.json
new file mode 100644
index 000000000..fe2a15703
--- /dev/null
+++ b/test/fixtures/tesla_mock/wedistribute-user.json
@@ -0,0 +1,31 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers"
+ }
+ ],
+ "type": "Organization",
+ "id": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog",
+ "following": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/following",
+ "followers": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/followers",
+ "liked": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/liked",
+ "inbox": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/inbox",
+ "outbox": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog/outbox",
+ "name": "We Distribute",
+ "preferredUsername": "blog",
+ "summary": "Connecting many threads in the federated web. We Distribute is an independent publication dedicated to the fediverse, decentralization, P2P technologies, and Free Software!
",
+ "url": "https://wedistribute.org/",
+ "publicKey": {
+ "id": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog#publicKey",
+ "owner": "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1bmUJ+y8PS8JFVi0KugN\r\nFl4pLvLog3V2lsV9ftmCXpveB/WJx66Tr1fQLsU3qYvQFc8UPGWD52zV4RENR1SN\r\nx0O6T2f97KUbRM+Ckow7Jyjtssgl+Mqq8UBZQ/+H8I/1Vpvt5E5hUykhFgwzx9qg\r\nzoIA3OK7alOpQbSoKXo0QcOh6yTRUnMSRMJAgUoZJzzXI/FmH/DtKr7ziQ1T2KWs\r\nVs8mWnTb/OlCxiheLuMlmJNMF+lPyVthvMIxF6Z5gV9d5QAmASSCI628e6uH2EUF\r\nDEEF5jo+Z5ffeNv28953lrnM+VB/wTjl3tYA+zCQeAmUPksX3E+YkXGxj+4rxBAY\r\n8wIDAQAB\r\n-----END PUBLIC KEY-----"
+ },
+ "manuallyApprovesFollowers": false,
+ "icon": {
+ "url": "https://wedistribute.org/wp-content/uploads/2019/02/b067de423757a538.png",
+ "type": "Image",
+ "mediaType": "image/png"
+ }
+}
diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs
index 482252cff..0ca87f035 100644
--- a/test/object/fetcher_test.exs
+++ b/test/object/fetcher_test.exs
@@ -110,6 +110,13 @@ test "it can fetch peertube videos" do
assert object
end
+ test "it can fetch wedistribute articles" do
+ {:ok, object} =
+ Fetcher.fetch_object_from_id("https://wedistribute.org/wp-json/pterotype/v1/object/85810")
+
+ assert object
+ end
+
test "all objects with fake directions are rejected by the object fetcher" do
assert {:error, _} =
Fetcher.fetch_and_contain_remote_object_from_id(
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 31c085e0d..55cfc1e00 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -301,6 +301,22 @@ def get("https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-
}}
end
+ def get("https://wedistribute.org/wp-json/pterotype/v1/object/85810", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/wedistribute-article.json")
+ }}
+ end
+
+ def get("https://wedistribute.org/wp-json/pterotype/v1/actor/-blog", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")
+ }}
+ end
+
def get("http://mastodon.example.org/users/admin", _, _, Accept: "application/activity+json") do
{:ok,
%Tesla.Env{
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index 3447c5b1f..0b167f839 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -300,6 +300,16 @@ test "attachments" do
assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object})
end
+ test "put the url advertised in the Activity in to the url attribute" do
+ id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
+ [activity] = Activity.search(nil, id)
+
+ status = StatusView.render("status.json", %{activity: activity})
+
+ assert status.uri == id
+ assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
+ end
+
test "a reblog" do
user = insert(:user)
activity = insert(:note_activity)
From 03c386614fabe9754ba82a1e89f6cf0ef518f61c Mon Sep 17 00:00:00 2001
From: Maksim
Date: Thu, 25 Jul 2019 03:43:13 +0000
Subject: [PATCH 042/202] fixed test for elixir 1.7.4
---
test/upload/filter/dedupe_test.exs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/upload/filter/dedupe_test.exs b/test/upload/filter/dedupe_test.exs
index fddd594dc..3de94dc20 100644
--- a/test/upload/filter/dedupe_test.exs
+++ b/test/upload/filter/dedupe_test.exs
@@ -25,7 +25,7 @@ test "adds shasum" do
assert {
:ok,
- %Pleroma.Upload{id: @shasum, path: "#{@shasum}.jpg"}
+ %Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"}
} = Dedupe.filter(upload)
end
end
From 7c8abbcb1ccafa79372fd75fdec87db2207b61ec Mon Sep 17 00:00:00 2001
From: "Haelwenn (lanodan) Monnier"
Date: Fri, 26 Jul 2019 15:33:25 +0200
Subject: [PATCH 043/202] CHANGELOG.md: Add entry for !1484
Related to: https://git.pleroma.social/pleroma/pleroma/merge_requests/1484
[ci skip]
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3f54d19e..bd3048b19 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Changed
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
+- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
- Configuration: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
From 6b77a88365f3a58cf8d1f9c00ed04532f706b87c Mon Sep 17 00:00:00 2001
From: Maksim
Date: Fri, 26 Jul 2019 20:27:38 +0000
Subject: [PATCH 044/202] [#1097] added redirect: /pleroma/admin ->
/pleroma/admin/
---
.../web/fallback_redirect_controller.ex | 77 +++++++++++++++++++
lib/pleroma/web/router.ex | 65 ----------------
test/web/fallback_test.exs | 4 +
3 files changed, 81 insertions(+), 65 deletions(-)
create mode 100644 lib/pleroma/web/fallback_redirect_controller.ex
diff --git a/lib/pleroma/web/fallback_redirect_controller.ex b/lib/pleroma/web/fallback_redirect_controller.ex
new file mode 100644
index 000000000..5fbf3695f
--- /dev/null
+++ b/lib/pleroma/web/fallback_redirect_controller.ex
@@ -0,0 +1,77 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Fallback.RedirectController do
+ use Pleroma.Web, :controller
+ require Logger
+ alias Pleroma.User
+ alias Pleroma.Web.Metadata
+
+ def api_not_implemented(conn, _params) do
+ conn
+ |> put_status(404)
+ |> json(%{error: "Not implemented"})
+ end
+
+ def redirector(conn, _params, code \\ 200)
+
+ # redirect to admin section
+ # /pleroma/admin -> /pleroma/admin/
+ #
+ def redirector(conn, %{"path" => ["pleroma", "admin"]} = _, _code) do
+ redirect(conn, to: "/pleroma/admin/")
+ end
+
+ def redirector(conn, _params, code) do
+ conn
+ |> put_resp_content_type("text/html")
+ |> send_file(code, index_file_path())
+ end
+
+ def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
+ with %User{} = user <- User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do
+ redirector_with_meta(conn, %{user: user})
+ else
+ nil ->
+ redirector(conn, params)
+ end
+ end
+
+ def redirector_with_meta(conn, params) do
+ {:ok, index_content} = File.read(index_file_path())
+
+ tags =
+ try do
+ Metadata.build_tags(params)
+ rescue
+ e ->
+ Logger.error(
+ "Metadata rendering for #{conn.request_path} failed.\n" <>
+ Exception.format(:error, e, __STACKTRACE__)
+ )
+
+ ""
+ end
+
+ response = String.replace(index_content, "", tags)
+
+ conn
+ |> put_resp_content_type("text/html")
+ |> send_resp(200, response)
+ end
+
+ def index_file_path do
+ Pleroma.Plugs.InstanceStatic.file_path("index.html")
+ end
+
+ def registration_page(conn, params) do
+ redirector(conn, params)
+ end
+
+ def empty(conn, _params) do
+ conn
+ |> put_status(204)
+ |> text("")
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index a9f3826fc..47ee762dc 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -729,68 +729,3 @@ defmodule Pleroma.Web.Router do
options("/*path", RedirectController, :empty)
end
end
-
-defmodule Fallback.RedirectController do
- use Pleroma.Web, :controller
- require Logger
- alias Pleroma.User
- alias Pleroma.Web.Metadata
-
- def api_not_implemented(conn, _params) do
- conn
- |> put_status(404)
- |> json(%{error: "Not implemented"})
- end
-
- def redirector(conn, _params, code \\ 200) do
- conn
- |> put_resp_content_type("text/html")
- |> send_file(code, index_file_path())
- end
-
- def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
- with %User{} = user <- User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do
- redirector_with_meta(conn, %{user: user})
- else
- nil ->
- redirector(conn, params)
- end
- end
-
- def redirector_with_meta(conn, params) do
- {:ok, index_content} = File.read(index_file_path())
-
- tags =
- try do
- Metadata.build_tags(params)
- rescue
- e ->
- Logger.error(
- "Metadata rendering for #{conn.request_path} failed.\n" <>
- Exception.format(:error, e, __STACKTRACE__)
- )
-
- ""
- end
-
- response = String.replace(index_content, "", tags)
-
- conn
- |> put_resp_content_type("text/html")
- |> send_resp(200, response)
- end
-
- def index_file_path do
- Pleroma.Plugs.InstanceStatic.file_path("index.html")
- end
-
- def registration_page(conn, params) do
- redirector(conn, params)
- end
-
- def empty(conn, _params) do
- conn
- |> put_status(204)
- |> text("")
- end
-end
diff --git a/test/web/fallback_test.exs b/test/web/fallback_test.exs
index cc78b3ae1..c13db9526 100644
--- a/test/web/fallback_test.exs
+++ b/test/web/fallback_test.exs
@@ -30,6 +30,10 @@ test "GET /api*path", %{conn: conn} do
|> json_response(404) == %{"error" => "Not implemented"}
end
+ test "GET /pleroma/admin -> /pleroma/admin/", %{conn: conn} do
+ assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/"
+ end
+
test "GET /*path", %{conn: conn} do
assert conn
|> get("/foo")
From 961e7785314688b9e2445649c71e12023a982165 Mon Sep 17 00:00:00 2001
From: Thomas Sileo
Date: Sun, 28 Jul 2019 14:17:56 +0200
Subject: [PATCH 045/202] Fix HTTP sig tweak on KeyId
---
lib/pleroma/signature.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex
index 0bf49fd7c..15bf3c317 100644
--- a/lib/pleroma/signature.ex
+++ b/lib/pleroma/signature.ex
@@ -15,7 +15,7 @@ def key_id_to_actor_id(key_id) do
|> Map.put(:fragment, nil)
uri =
- if String.ends_with?(uri.path, "/publickey") do
+ if not is_nil(uri.path) and String.ends_with?(uri.path, "/publickey") do
Map.put(uri, :path, String.replace(uri.path, "/publickey", ""))
else
uri
From 02dc651828af00ba88a687570833333d4b939c7e Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Sun, 28 Jul 2019 20:24:39 +0000
Subject: [PATCH 046/202] Handle 303 redirects
---
lib/pleroma/http/connection.ex | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex
index a1460d303..7e2c6f5e8 100644
--- a/lib/pleroma/http/connection.ex
+++ b/lib/pleroma/http/connection.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.HTTP.Connection do
connect_timeout: 10_000,
recv_timeout: 20_000,
follow_redirect: true,
+ force_redirect: true,
pool: :federation
]
@adapter Application.get_env(:tesla, :adapter)
From 6a4b8b2681023dc355331999aeac6c24c5a21f7f Mon Sep 17 00:00:00 2001
From: Maksim
Date: Sun, 28 Jul 2019 20:29:26 +0000
Subject: [PATCH 047/202] fixed User.update_and_set_cache for stale user
---
lib/pleroma/user.ex | 2 +-
test/user_test.exs | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 974f6df18..6e2fd3af8 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -471,7 +471,7 @@ def set_cache(%User{} = user) do
end
def update_and_set_cache(changeset) do
- with {:ok, user} <- Repo.update(changeset) do
+ with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
set_cache(user)
else
e -> e
diff --git a/test/user_test.exs b/test/user_test.exs
index 8a7b7537f..556df45fd 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -1369,4 +1369,28 @@ test "user with internal-prefixed nickname returns true" do
assert User.is_internal_user?(user)
end
end
+
+ describe "update_and_set_cache/1" do
+ test "returns error when user is stale instead Ecto.StaleEntryError" do
+ user = insert(:user)
+
+ changeset = Ecto.Changeset.change(user, bio: "test")
+
+ Repo.delete(user)
+
+ assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} =
+ User.update_and_set_cache(changeset)
+ end
+
+ test "performs update cache if user updated" do
+ user = insert(:user)
+ assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
+
+ changeset = Ecto.Changeset.change(user, bio: "test-bio")
+
+ assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset)
+ assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
+ assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
+ end
+ end
end
From 242f5c585ed797917ba8c61ceb5d266f4c670c90 Mon Sep 17 00:00:00 2001
From: Sachin Joshi
Date: Sun, 28 Jul 2019 20:30:10 +0000
Subject: [PATCH 048/202] add account confirmation email resend in mastodon api
---
CHANGELOG.md | 1 +
config/config.exs | 3 +-
docs/api/pleroma_api.md | 8 ++++
.../mastodon_api/mastodon_api_controller.ex | 14 +++++++
lib/pleroma/web/router.ex | 6 +++
.../mastodon_api_controller_test.exs | 41 +++++++++++++++++++
6 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd3048b19..20f4eea41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub: Add an internal service actor for fetching ActivityPub objects.
- ActivityPub: Optional signing of ActivityPub object fetches.
- Admin API: Endpoint for fetching latest user's statuses
+- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=` for resending account confirmation.
### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
diff --git a/config/config.exs b/config/config.exs
index 569411866..17770640a 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -534,7 +534,8 @@
relation_id_action: {60_000, 2},
statuses_actions: {10_000, 15},
status_id_action: {60_000, 3},
- password_reset: {1_800_000, 5}
+ password_reset: {1_800_000, 5},
+ account_confirmation_resend: {8_640_000, 5}
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md
index d83ebd734..5698e88ac 100644
--- a/docs/api/pleroma_api.md
+++ b/docs/api/pleroma_api.md
@@ -245,6 +245,14 @@ See [Admin-API](Admin-API.md)
- PATCH `/api/v1/pleroma/accounts/update_banner`: Set/clear user banner image
- PATCH `/api/v1/pleroma/accounts/update_background`: Set/clear user background image
+## `/api/v1/pleroma/accounts/confirmation_resend`
+### Resend confirmation email
+* Method `POST`
+* Params:
+ * `email`: email of that needs to be verified
+* Authentication: not required
+* Response: 204 No Content
+
## `/api/v1/pleroma/mascot`
### Gets user mascot image
* Method `GET`
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index d660f3f05..5bdbb709b 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -4,6 +4,9 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
alias Ecto.Changeset
alias Pleroma.Activity
alias Pleroma.Bookmark
@@ -74,6 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
plug(RateLimiter, :app_account_creation when action == :account_register)
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
plug(RateLimiter, :password_reset when action == :password_reset)
+ plug(RateLimiter, :account_confirmation_resend when action == :account_confirmation_resend)
@local_mastodon_name "Mastodon-Local"
@@ -1839,6 +1843,16 @@ def password_reset(conn, params) do
end
end
+ def account_confirmation_resend(conn, params) do
+ nickname_or_email = params["email"] || params["nickname"]
+
+ with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email),
+ {:ok, _} <- User.try_send_confirmation_email(user) do
+ conn
+ |> json_response(:no_content, "")
+ end
+ end
+
def try_render(conn, target, params)
when is_binary(target) do
case render(conn, target, params) do
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 47ee762dc..4e1ab6c33 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -412,6 +412,12 @@ defmodule Pleroma.Web.Router do
get("/accounts/search", SearchController, :account_search)
+ post(
+ "/pleroma/accounts/confirmation_resend",
+ MastodonAPIController,
+ :account_confirmation_resend
+ )
+
scope [] do
pipe_through(:oauth_read_or_public)
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index ce2e44499..d7f92fac2 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -3923,4 +3923,45 @@ test "it returns 400 when user is not local", %{conn: conn, user: user} do
assert conn.resp_body == ""
end
end
+
+ describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :account_activation_required])
+
+ unless setting do
+ Pleroma.Config.put([:instance, :account_activation_required], true)
+ on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
+ end
+
+ user = insert(:user)
+ info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
+
+ {:ok, user} =
+ user
+ |> Changeset.change()
+ |> Changeset.put_embed(:info, info_change)
+ |> Repo.update()
+
+ assert user.info.confirmation_pending
+
+ [user: user]
+ end
+
+ test "resend account confirmation email", %{conn: conn, user: user} do
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
+ |> json_response(:no_content)
+
+ email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
+ end
+ end
end
From 492d854e7aa29a2438dbbe2f95e509e43328eb7f Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Sun, 28 Jul 2019 21:29:10 +0000
Subject: [PATCH 049/202] transmogrifier: use User.delete() instead of
handrolled user deletion code for remote users
Closes #1104
---
CHANGELOG.md | 1 +
.../web/activity_pub/transmogrifier.ex | 15 +----
test/notification_test.exs | 58 +++++++++++++++++++
3 files changed, 60 insertions(+), 14 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20f4eea41..48379b757 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rich Media: Parser failing when no TTL can be found by image TTL setters
- Rich Media: The crawled URL is now spliced into the rich media data.
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
+- ActivityPub S2S: remote user deletions now work the same as local user deletions.
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 602ae48e1..7f06e6edd 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -656,20 +656,7 @@ def handle_incoming(
nil ->
case User.get_cached_by_ap_id(object_id) do
%User{ap_id: ^actor} = user ->
- {:ok, followers} = User.get_followers(user)
-
- Enum.each(followers, fn follower ->
- User.unfollow(follower, user)
- end)
-
- {:ok, friends} = User.get_friends(user)
-
- Enum.each(friends, fn followed ->
- User.unfollow(user, followed)
- end)
-
- User.invalidate_cache(user)
- Repo.delete(user)
+ User.delete(user)
nil ->
:error
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 28f8df49d..c88ac54bd 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -564,6 +564,64 @@ test "replying to a deleted post without tagging does not generate a notificatio
assert Enum.empty?(Notification.for_user(user))
end
+
+ test "notifications are deleted if a local user is deleted" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}", "visibility" => "direct"})
+
+ refute Enum.empty?(Notification.for_user(other_user))
+
+ User.delete(user)
+
+ assert Enum.empty?(Notification.for_user(other_user))
+ end
+
+ test "notifications are deleted if a remote user is deleted" do
+ remote_user = insert(:user)
+ local_user = insert(:user)
+
+ dm_message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Create",
+ "actor" => remote_user.ap_id,
+ "id" => remote_user.ap_id <> "/activities/test",
+ "to" => [local_user.ap_id],
+ "cc" => [],
+ "object" => %{
+ "type" => "Note",
+ "content" => "Hello!",
+ "tag" => [
+ %{
+ "type" => "Mention",
+ "href" => local_user.ap_id,
+ "name" => "@#{local_user.nickname}"
+ }
+ ],
+ "to" => [local_user.ap_id],
+ "cc" => [],
+ "attributedTo" => remote_user.ap_id
+ }
+ }
+
+ {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
+
+ refute Enum.empty?(Notification.for_user(local_user))
+
+ delete_user_message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "id" => remote_user.ap_id <> "/activities/delete",
+ "actor" => remote_user.ap_id,
+ "type" => "Delete",
+ "object" => remote_user.ap_id
+ }
+
+ {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
+
+ assert Enum.empty?(Notification.for_user(local_user))
+ end
end
describe "for_user" do
From 9d78b3b281ff7f758d4e0dce19fd74d938e47ccc Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Mon, 29 Jul 2019 02:12:35 +0000
Subject: [PATCH 050/202] mix: add ex_const dependency
---
mix.exs | 1 +
mix.lock | 1 +
2 files changed, 2 insertions(+)
diff --git a/mix.exs b/mix.exs
index e69940c5d..2a8fe2e9d 100644
--- a/mix.exs
+++ b/mix.exs
@@ -150,6 +150,7 @@ defp deps do
{:benchee, "~> 1.0"},
{:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},
{:ex_rated, "~> 1.3"},
+ {:ex_const, "~> 0.2"},
{:plug_static_index_html, "~> 1.0.0"},
{:excoveralls, "~> 0.11.1", only: :test},
{:mox, "~> 0.5", only: :test}
diff --git a/mix.lock b/mix.lock
index 5f20878d3..65da7be8b 100644
--- a/mix.lock
+++ b/mix.lock
@@ -27,6 +27,7 @@
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
"ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},
+ "ex_const": {:hex, :ex_const, "0.2.4", "d06e540c9d834865b012a17407761455efa71d0ce91e5831e86881b9c9d82448", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"},
"ex_rated": {:hex, :ex_rated, "1.3.3", "30ecbdabe91f7eaa9d37fa4e81c85ba420f371babeb9d1910adbcd79ec798d27", [:mix], [{:ex2ms, "~> 1.5", [hex: :ex2ms, repo: "hexpm", optional: false]}], "hexpm"},
From b93498eb5289dc92587b77c316ed9f697bb9e5c8 Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Mon, 29 Jul 2019 02:43:19 +0000
Subject: [PATCH 051/202] constants: add as_public constant and use it
everywhere
---
lib/mix/tasks/pleroma/database.ex | 12 ++++++---
lib/pleroma/activity/search.ex | 4 ++-
lib/pleroma/constants.ex | 9 +++++++
lib/pleroma/web/activity_pub/activity_pub.ex | 27 +++++++------------
.../web/activity_pub/mrf/hellthread_policy.ex | 11 +++++---
.../web/activity_pub/mrf/keyword_policy.ex | 8 +++---
.../web/activity_pub/mrf/reject_non_public.ex | 6 ++---
.../web/activity_pub/mrf/simple_policy.ex | 12 ++++-----
.../web/activity_pub/mrf/tag_policy.ex | 15 ++++++-----
lib/pleroma/web/activity_pub/publisher.ex | 6 ++---
.../web/activity_pub/transmogrifier.ex | 11 ++++----
lib/pleroma/web/activity_pub/utils.ex | 13 ++++-----
lib/pleroma/web/activity_pub/visibility.ex | 8 +++---
lib/pleroma/web/auth/authenticator.ex | 3 +--
lib/pleroma/web/common_api/common_api.ex | 3 +--
lib/pleroma/web/common_api/utils.ex | 5 ++--
.../mastodon_api/mastodon_api_controller.ex | 6 ++---
lib/pleroma/web/oauth/oauth_controller.ex | 3 +--
lib/pleroma/web/oauth/token.ex | 3 +--
.../web/ostatus/activity_representer.ex | 3 ++-
.../web/ostatus/handlers/note_handler.ex | 5 ++--
.../rich_media/parsers/ttl/aws_signed_url.ex | 3 +--
lib/pleroma/web/twitter_api/twitter_api.ex | 4 ++-
.../web/twitter_api/views/activity_view.ex | 3 ++-
.../twitter_api/views/notification_view.ex | 4 ++-
test/web/push/impl_test.exs | 6 ++---
26 files changed, 104 insertions(+), 89 deletions(-)
create mode 100644 lib/pleroma/constants.ex
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex
index e91fb31d1..8547a329a 100644
--- a/lib/mix/tasks/pleroma/database.ex
+++ b/lib/mix/tasks/pleroma/database.ex
@@ -8,6 +8,7 @@ defmodule Mix.Tasks.Pleroma.Database do
alias Pleroma.Repo
alias Pleroma.User
require Logger
+ require Pleroma.Constants
import Mix.Pleroma
use Mix.Task
@@ -99,10 +100,15 @@ def run(["prune_objects" | args]) do
NaiveDateTime.utc_now()
|> NaiveDateTime.add(-(deadline * 86_400))
- public = "https://www.w3.org/ns/activitystreams#Public"
-
from(o in Object,
- where: fragment("?->'to' \\? ? OR ?->'cc' \\? ?", o.data, ^public, o.data, ^public),
+ where:
+ fragment(
+ "?->'to' \\? ? OR ?->'cc' \\? ?",
+ o.data,
+ ^Pleroma.Constants.as_public(),
+ o.data,
+ ^Pleroma.Constants.as_public()
+ ),
where: o.inserted_at < ^time_deadline,
where:
fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex
index 0cc3770a7..f847ac238 100644
--- a/lib/pleroma/activity/search.ex
+++ b/lib/pleroma/activity/search.ex
@@ -9,6 +9,8 @@ defmodule Pleroma.Activity.Search do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Visibility
+ require Pleroma.Constants
+
import Ecto.Query
def search(user, search_query, options \\ []) do
@@ -39,7 +41,7 @@ def maybe_restrict_author(query, _), do: query
defp restrict_public(q) do
from([a, o] in q,
where: fragment("?->>'type' = 'Create'", a.data),
- where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients
+ where: ^Pleroma.Constants.as_public() in a.recipients
)
end
diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex
new file mode 100644
index 000000000..ef1418543
--- /dev/null
+++ b/lib/pleroma/constants.ex
@@ -0,0 +1,9 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Constants do
+ use Const
+
+ const(as_public, do: "https://www.w3.org/ns/activitystreams#Public")
+end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index a42c50875..6fd7fef92 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -23,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
import Pleroma.Web.ActivityPub.Visibility
require Logger
+ require Pleroma.Constants
# For Announce activities, we filter the recipients based on following status for any actors
# that match actual users. See issue #164 for more information about why this is necessary.
@@ -207,8 +208,6 @@ def stream_out_participations(%Object{data: %{"context" => context}}, user) do
def stream_out_participations(_, _), do: :noop
def stream_out(activity) do
- public = "https://www.w3.org/ns/activitystreams#Public"
-
if activity.data["type"] in ["Create", "Announce", "Delete"] do
object = Object.normalize(activity)
# Do not stream out poll replies
@@ -216,7 +215,7 @@ def stream_out(activity) do
Pleroma.Web.Streamer.stream("user", activity)
Pleroma.Web.Streamer.stream("list", activity)
- if Enum.member?(activity.data["to"], public) do
+ if get_visibility(activity) == "public" do
Pleroma.Web.Streamer.stream("public", activity)
if activity.local do
@@ -238,13 +237,8 @@ def stream_out(activity) do
end
end
else
- # TODO: Write test, replace with visibility test
- if !Enum.member?(activity.data["cc"] || [], public) &&
- !Enum.member?(
- activity.data["to"],
- User.get_cached_by_ap_id(activity.data["actor"]).follower_address
- ),
- do: Pleroma.Web.Streamer.stream("direct", activity)
+ if get_visibility(activity) == "direct",
+ do: Pleroma.Web.Streamer.stream("direct", activity)
end
end
end
@@ -514,7 +508,7 @@ def flag(
end
defp fetch_activities_for_context_query(context, opts) do
- public = ["https://www.w3.org/ns/activitystreams#Public"]
+ public = [Pleroma.Constants.as_public()]
recipients =
if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
@@ -555,7 +549,7 @@ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
end
def fetch_public_activities(opts \\ %{}) do
- q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
+ q = fetch_activities_query([Pleroma.Constants.as_public()], opts)
q
|> restrict_unlisted()
@@ -646,10 +640,9 @@ defp user_activities_recipients(%{"godmode" => true}) do
defp user_activities_recipients(%{"reading_user" => reading_user}) do
if reading_user do
- ["https://www.w3.org/ns/activitystreams#Public"] ++
- [reading_user.ap_id | reading_user.following]
+ [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
else
- ["https://www.w3.org/ns/activitystreams#Public"]
+ [Pleroma.Constants.as_public()]
end
end
@@ -834,7 +827,7 @@ defp restrict_unlisted(query) do
fragment(
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
activity.data,
- ^["https://www.w3.org/ns/activitystreams#Public"]
+ ^[Pleroma.Constants.as_public()]
)
)
end
@@ -971,7 +964,7 @@ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
where:
fragment("? && ?", activity.recipients, ^recipients) or
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
- "https://www.w3.org/ns/activitystreams#Public" in activity.recipients)
+ ^Pleroma.Constants.as_public() in activity.recipients)
)
end
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index a699f6a7e..377987cf2 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -4,6 +4,9 @@
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
alias Pleroma.User
+
+ require Pleroma.Constants
+
@moduledoc "Block messages with too much mentions (configurable)"
@behaviour Pleroma.Web.ActivityPub.MRF
@@ -19,12 +22,12 @@ defp delist_message(message, threshold) when threshold > 0 do
when follower_collection? and recipients > threshold ->
message
|> Map.put("to", [follower_collection])
- |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"])
+ |> Map.put("cc", [Pleroma.Constants.as_public()])
{:public, recipients} when recipients > threshold ->
message
|> Map.put("to", [])
- |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"])
+ |> Map.put("cc", [Pleroma.Constants.as_public()])
_ ->
message
@@ -51,10 +54,10 @@ defp get_recipient_count(message) do
recipients = (message["to"] || []) ++ (message["cc"] || [])
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
- if Enum.member?(recipients, "https://www.w3.org/ns/activitystreams#Public") do
+ if Enum.member?(recipients, Pleroma.Constants.as_public()) do
recipients =
recipients
- |> List.delete("https://www.w3.org/ns/activitystreams#Public")
+ |> List.delete(Pleroma.Constants.as_public())
|> List.delete(follower_collection)
{:public, length(recipients)}
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
index d5c341433..4eec8b916 100644
--- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
@@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
+ require Pleroma.Constants
+
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
@behaviour Pleroma.Web.ActivityPub.MRF
@@ -31,12 +33,12 @@ defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} =
defp check_ftl_removal(
%{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message
) do
- if "https://www.w3.org/ns/activitystreams#Public" in to and
+ if Pleroma.Constants.as_public() in to and
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
string_matches?(content, pattern) or string_matches?(summary, pattern)
end) do
- to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public")
- cc = ["https://www.w3.org/ns/activitystreams#Public" | message["cc"] || []]
+ to = List.delete(to, Pleroma.Constants.as_public())
+ cc = [Pleroma.Constants.as_public() | message["cc"] || []]
message =
message
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index da13fd7c7..457b6ee10 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
@behaviour Pleroma.Web.ActivityPub.MRF
- @public "https://www.w3.org/ns/activitystreams#Public"
+ require Pleroma.Constants
@impl true
def filter(%{"type" => "Create"} = object) do
@@ -19,8 +19,8 @@ def filter(%{"type" => "Create"} = object) do
# Determine visibility
visibility =
cond do
- @public in object["to"] -> "public"
- @public in object["cc"] -> "unlisted"
+ Pleroma.Constants.as_public() in object["to"] -> "public"
+ Pleroma.Constants.as_public() in object["cc"] -> "unlisted"
user.follower_address in object["to"] -> "followers"
true -> "direct"
end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 2cf63d3db..f266457e3 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -8,6 +8,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@moduledoc "Filter activities depending on their origin instance"
@behaviour MRF
+ require Pleroma.Constants
+
defp check_accept(%{host: actor_host} = _actor_info, object) do
accepts =
Pleroma.Config.get([:mrf_simple, :accept])
@@ -89,14 +91,10 @@ defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
object =
with true <- MRF.subdomain_match?(timeline_removal, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]),
- true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
- to =
- List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
- [user.follower_address]
+ true <- Pleroma.Constants.as_public() in object["to"] do
+ to = List.delete(object["to"], Pleroma.Constants.as_public()) ++ [user.follower_address]
- cc =
- List.delete(object["cc"], user.follower_address) ++
- ["https://www.w3.org/ns/activitystreams#Public"]
+ cc = List.delete(object["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()]
object
|> Map.put("to", to)
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index b42c4ed76..70edf4f7f 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `mrf_tag:disable-any-subscription`: Reject any follow requests
"""
- @public "https://www.w3.org/ns/activitystreams#Public"
+ require Pleroma.Constants
defp get_tags(%User{tags: tags}) when is_list(tags), do: tags
defp get_tags(_), do: []
@@ -70,9 +70,9 @@ defp process_tag(
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, @public) do
- to = List.delete(to, @public) ++ [user.follower_address]
- cc = List.delete(cc, user.follower_address) ++ [@public]
+ if Enum.member?(to, Pleroma.Constants.as_public()) do
+ to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
+ cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
object =
object
@@ -103,9 +103,10 @@ defp process_tag(
) do
user = User.get_cached_by_ap_id(actor)
- if Enum.member?(to, @public) or Enum.member?(cc, @public) do
- to = List.delete(to, @public) ++ [user.follower_address]
- cc = List.delete(cc, @public)
+ if Enum.member?(to, Pleroma.Constants.as_public()) or
+ Enum.member?(cc, Pleroma.Constants.as_public()) do
+ to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
+ cc = List.delete(cc, Pleroma.Constants.as_public())
object =
object
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 016d78216..46edab0bd 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
+ require Pleroma.Constants
+
import Pleroma.Web.ActivityPub.Visibility
@behaviour Pleroma.Web.Federator.Publisher
@@ -117,8 +119,6 @@ defp get_cc_ap_ids(ap_id, recipients) do
|> Enum.map(& &1.ap_id)
end
- @as_public "https://www.w3.org/ns/activitystreams#Public"
-
defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
@@ -145,7 +145,7 @@ def determine_inbox(
type == "Delete" ->
maybe_use_sharedinbox(user)
- @as_public in to || @as_public in cc ->
+ Pleroma.Constants.as_public() in to || Pleroma.Constants.as_public() in cc ->
maybe_use_sharedinbox(user)
length(to) + length(cc) > 1 ->
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 7f06e6edd..44bb1cb9a 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
import Ecto.Query
require Logger
+ require Pleroma.Constants
@doc """
Modifies an incoming AP object (mastodon format) to our internal format.
@@ -102,8 +103,7 @@ def fix_explicit_addressing(object) do
follower_collection = User.get_cached_by_ap_id(Containment.get_actor(object)).follower_address
- explicit_mentions =
- explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public", follower_collection]
+ explicit_mentions = explicit_mentions ++ [Pleroma.Constants.as_public(), follower_collection]
fix_explicit_addressing(object, explicit_mentions, follower_collection)
end
@@ -115,11 +115,11 @@ def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collec
if followers_collection not in recipients do
cond do
- "https://www.w3.org/ns/activitystreams#Public" in cc ->
+ Pleroma.Constants.as_public() in cc ->
to = to ++ [followers_collection]
Map.put(object, "to", to)
- "https://www.w3.org/ns/activitystreams#Public" in to ->
+ Pleroma.Constants.as_public() in to ->
cc = cc ++ [followers_collection]
Map.put(object, "cc", cc)
@@ -480,8 +480,7 @@ def handle_incoming(
{:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]),
- {_, false} <-
- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
+ {_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},
{_, false} <- {:user_locked, User.locked?(followed)},
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
{_, {:ok, _}} <-
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index c146f59d4..39074888b 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -18,6 +18,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
import Ecto.Query
require Logger
+ require Pleroma.Constants
@supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]
@supported_report_states ~w(open closed resolved)
@@ -418,7 +419,7 @@ def make_follow_data(
"type" => "Follow",
"actor" => follower_id,
"to" => [followed_id],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"object" => followed_id,
"state" => "pending"
}
@@ -510,7 +511,7 @@ def make_announce_data(
"actor" => ap_id,
"object" => id,
"to" => [user.follower_address, object.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => object.data["context"]
}
@@ -530,7 +531,7 @@ def make_unannounce_data(
"actor" => ap_id,
"object" => activity.data,
"to" => [user.follower_address, activity.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => context
}
@@ -547,7 +548,7 @@ def make_unlike_data(
"actor" => ap_id,
"object" => activity.data,
"to" => [user.follower_address, activity.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => context
}
@@ -556,7 +557,7 @@ def make_unlike_data(
def add_announce_to_object(
%Activity{
- data: %{"actor" => actor, "cc" => ["https://www.w3.org/ns/activitystreams#Public"]}
+ data: %{"actor" => actor, "cc" => [Pleroma.Constants.as_public()]}
},
object
) do
@@ -765,7 +766,7 @@ defp get_updated_targets(
) do
cc = Map.get(data, "cc", [])
follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address
- public = "https://www.w3.org/ns/activitystreams#Public"
+ public = Pleroma.Constants.as_public()
case visibility do
"public" ->
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 097fceb08..dfb166b65 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -8,14 +8,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Repo
alias Pleroma.User
- @public "https://www.w3.org/ns/activitystreams#Public"
+ require Pleroma.Constants
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
def is_public?(%Object{data: data}), do: is_public?(data)
def is_public?(%Activity{data: data}), do: is_public?(data)
def is_public?(%{"directMessage" => true}), do: false
- def is_public?(data), do: @public in (data["to"] ++ (data["cc"] || []))
+ def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))
def is_private?(activity) do
with false <- is_public?(activity),
@@ -73,10 +73,10 @@ def get_visibility(object) do
cc = object.data["cc"] || []
cond do
- @public in to ->
+ Pleroma.Constants.as_public() in to ->
"public"
- @public in cc ->
+ Pleroma.Constants.as_public() in cc ->
"unlisted"
# this should use the sql for the object's activity
diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex
index d4e0ffa80..dd49987f7 100644
--- a/lib/pleroma/web/auth/authenticator.ex
+++ b/lib/pleroma/web/auth/authenticator.ex
@@ -21,8 +21,7 @@ def get_user(plug), do: implementation().get_user(plug)
def create_from_registration(plug, registration),
do: implementation().create_from_registration(plug, registration)
- @callback get_registration(Plug.Conn.t()) ::
- {:ok, Registration.t()} | {:error, any()}
+ @callback get_registration(Plug.Conn.t()) :: {:ok, Registration.t()} | {:error, any()}
def get_registration(plug), do: implementation().get_registration(plug)
@callback handle_error(Plug.Conn.t(), any()) :: any()
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 44af6a773..2db58324b 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -300,8 +300,7 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
}
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
true <- Visibility.is_public?(activity),
- %{valid?: true} = info_changeset <-
- User.Info.add_pinnned_activity(user.info, activity),
+ %{valid?: true} = info_changeset <- User.Info.add_pinnned_activity(user.info, activity),
changeset <-
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
{:ok, _user} <- User.update_and_set_cache(changeset) do
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 94462c3dd..d80fffa26 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.Web.MediaProxy
require Logger
+ require Pleroma.Constants
# This is a hack for twidere.
def get_by_id_or_ap_id(id) do
@@ -66,7 +67,7 @@ def attachments_from_ids_descs(ids, descs_str) do
@spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
{list(String.t()), list(String.t())}
def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
- to = ["https://www.w3.org/ns/activitystreams#Public" | mentioned_users]
+ to = [Pleroma.Constants.as_public() | mentioned_users]
cc = [user.follower_address]
if inReplyTo do
@@ -78,7 +79,7 @@ def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do
to = [user.follower_address | mentioned_users]
- cc = ["https://www.w3.org/ns/activitystreams#Public"]
+ cc = [Pleroma.Constants.as_public()]
if inReplyTo do
{Enum.uniq([inReplyTo.data["actor"] | to]), cc}
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 5bdbb709b..174e93468 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -49,6 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
import Ecto.Query
require Logger
+ require Pleroma.Constants
@rate_limited_relations_actions ~w(follow unfollow)a
@@ -1224,10 +1225,9 @@ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params
recipients =
if for_user do
- ["https://www.w3.org/ns/activitystreams#Public"] ++
- [for_user.ap_id | for_user.following]
+ [Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following]
else
- ["https://www.w3.org/ns/activitystreams#Public"]
+ [Pleroma.Constants.as_public()]
end
activities =
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index ef53b7ae3..81eae2c8b 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -365,8 +365,7 @@ def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs})
def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "connect"} = params) do
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
%Registration{} = registration <- Repo.get(Registration, registration_id),
- {_, {:ok, auth}} <-
- {:create_authorization, do_create_authorization(conn, params)},
+ {_, {:ok, auth}} <- {:create_authorization, do_create_authorization(conn, params)},
%User{} = user <- Repo.preload(auth, :user).user,
{:ok, _updated_registration} <- Registration.bind_to_user(registration, user) do
conn
diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex
index 90c304487..40f131b57 100644
--- a/lib/pleroma/web/oauth/token.ex
+++ b/lib/pleroma/web/oauth/token.ex
@@ -44,8 +44,7 @@ def get_by_refresh_token(%App{id: app_id} = _app, token) do
|> Repo.find_resource()
end
- @spec exchange_token(App.t(), Authorization.t()) ::
- {:ok, Token.t()} | {:error, Changeset.t()}
+ @spec exchange_token(App.t(), Authorization.t()) :: {:ok, Token.t()} | {:error, Changeset.t()}
def exchange_token(app, auth) do
with {:ok, auth} <- Authorization.use_token(auth),
true <- auth.app_id == app.id do
diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex
index 95037125d..760345301 100644
--- a/lib/pleroma/web/ostatus/activity_representer.ex
+++ b/lib/pleroma/web/ostatus/activity_representer.ex
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
alias Pleroma.Web.OStatus.UserRepresenter
require Logger
+ require Pleroma.Constants
defp get_href(id) do
with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
@@ -34,7 +35,7 @@ defp get_mentions(to) do
Enum.map(to, fn id ->
cond do
# Special handling for the AP/Ostatus public collections
- "https://www.w3.org/ns/activitystreams#Public" == id ->
+ Pleroma.Constants.as_public() == id ->
{:link,
[
rel: "mentioned",
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
index 8e0adad91..3005e8f57 100644
--- a/lib/pleroma/web/ostatus/handlers/note_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.OStatus.NoteHandler do
require Logger
+ require Pleroma.Constants
alias Pleroma.Activity
alias Pleroma.Object
@@ -49,7 +50,7 @@ def get_people_mentions(entry) do
def get_collection_mentions(entry) do
transmogrify = fn
"http://activityschema.org/collection/public" ->
- "https://www.w3.org/ns/activitystreams#Public"
+ Pleroma.Constants.as_public()
group ->
group
@@ -126,7 +127,7 @@ def handle_note(entry, doc \\ nil, options \\ []) do
to <- make_to_list(actor, mentions),
date <- XML.string_from_xpath("//published", entry),
unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
- cc <- if(unlisted, do: ["https://www.w3.org/ns/activitystreams#Public"], else: []),
+ cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []),
note <-
CommonAPI.Utils.make_note_data(
actor.ap_id,
diff --git a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex
index 014c0935f..0dc1efdaf 100644
--- a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex
+++ b/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex
@@ -19,8 +19,7 @@ defp is_aws_signed_url(nil), do: nil
defp is_aws_signed_url(image) when is_binary(image) do
%URI{host: host, query: query} = URI.parse(image)
- if String.contains?(host, "amazonaws.com") and
- String.contains?(query, "X-Amz-Expires") do
+ if String.contains?(host, "amazonaws.com") and String.contains?(query, "X-Amz-Expires") do
image
else
nil
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index bb5dda204..80082ea84 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
import Ecto.Query
+ require Pleroma.Constants
+
def create_status(%User{} = user, %{"status" => _} = data) do
CommonAPI.post(user, data)
end
@@ -286,7 +288,7 @@ def search(_user, %{"q" => query} = params) do
from(
[a, o] in Activity.with_preloaded_object(Activity),
where: fragment("?->>'type' = 'Create'", a.data),
- where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
+ where: ^Pleroma.Constants.as_public() in a.recipients,
where:
fragment(
"to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)",
diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex
index e84af84dc..abae63877 100644
--- a/lib/pleroma/web/twitter_api/views/activity_view.ex
+++ b/lib/pleroma/web/twitter_api/views/activity_view.ex
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
import Ecto.Query
require Logger
+ require Pleroma.Constants
defp query_context_ids([]), do: []
@@ -91,7 +92,7 @@ defp get_user(ap_id, opts) do
String.ends_with?(ap_id, "/followers") ->
nil
- ap_id == "https://www.w3.org/ns/activitystreams#Public" ->
+ ap_id == Pleroma.Constants.as_public() ->
nil
user = User.get_cached_by_ap_id(ap_id) ->
diff --git a/lib/pleroma/web/twitter_api/views/notification_view.ex b/lib/pleroma/web/twitter_api/views/notification_view.ex
index e7c7a7496..085cd5aa3 100644
--- a/lib/pleroma/web/twitter_api/views/notification_view.ex
+++ b/lib/pleroma/web/twitter_api/views/notification_view.ex
@@ -10,6 +10,8 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do
alias Pleroma.Web.TwitterAPI.ActivityView
alias Pleroma.Web.TwitterAPI.UserView
+ require Pleroma.Constants
+
defp get_user(ap_id, opts) do
cond do
user = opts[:users][ap_id] ->
@@ -18,7 +20,7 @@ defp get_user(ap_id, opts) do
String.ends_with?(ap_id, "/followers") ->
nil
- ap_id == "https://www.w3.org/ns/activitystreams#Public" ->
+ ap_id == Pleroma.Constants.as_public() ->
nil
true ->
diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs
index 1e948086a..e2f89f40a 100644
--- a/test/web/push/impl_test.exs
+++ b/test/web/push/impl_test.exs
@@ -124,8 +124,7 @@ test "renders body for follow activity" do
{:ok, _, _, activity} = CommonAPI.follow(user, other_user)
object = Object.normalize(activity)
- assert Impl.format_body(%{activity: activity}, user, object) ==
- "@Bob has followed you"
+ assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has followed you"
end
test "renders body for announce activity" do
@@ -156,7 +155,6 @@ test "renders body for like activity" do
{:ok, activity, _} = CommonAPI.favorite(activity.id, user)
object = Object.normalize(activity)
- assert Impl.format_body(%{activity: activity}, user, object) ==
- "@Bob has favorited your post"
+ assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"
end
end
From 159bbec570c308bf3d71487726737a91eb569178 Mon Sep 17 00:00:00 2001
From: Maksim
Date: Mon, 29 Jul 2019 05:02:20 +0000
Subject: [PATCH 052/202] added tests for OstatusController
---
lib/pleroma/web/ostatus/ostatus_controller.ex | 170 +++--
test/web/ostatus/ostatus_controller_test.exs | 630 ++++++++++++++----
2 files changed, 585 insertions(+), 215 deletions(-)
diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex
index 372d52899..c70063b84 100644
--- a/lib/pleroma/web/ostatus/ostatus_controller.ex
+++ b/lib/pleroma/web/ostatus/ostatus_controller.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.OStatus.OStatusController do
use Pleroma.Web, :controller
+ alias Fallback.RedirectController
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.User
@@ -12,42 +13,44 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.Federator
+ alias Pleroma.Web.Metadata.PlayerView
alias Pleroma.Web.OStatus
alias Pleroma.Web.OStatus.ActivityRepresenter
alias Pleroma.Web.OStatus.FeedRepresenter
+ alias Pleroma.Web.Router
alias Pleroma.Web.XML
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
+ plug(
+ Pleroma.Plugs.SetFormatPlug
+ when action in [:feed_redirect, :object, :activity, :notice]
+ )
+
action_fallback(:errors)
+ def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
+ with {_, %User{} = user} <-
+ {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
+ RedirectController.redirector_with_meta(conn, %{user: user})
+ end
+ end
+
+ def feed_redirect(%{assigns: %{format: format}} = conn, _params)
+ when format in ["json", "activity+json"] do
+ ActivityPubController.call(conn, :user)
+ end
+
def feed_redirect(conn, %{"nickname" => nickname}) do
- case get_format(conn) do
- "html" ->
- with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
- Fallback.RedirectController.redirector_with_meta(conn, %{user: user})
- else
- nil -> {:error, :not_found}
- end
-
- "activity+json" ->
- ActivityPubController.call(conn, :user)
-
- "json" ->
- ActivityPubController.call(conn, :user)
-
- _ ->
- with %User{} = user <- User.get_cached_by_nickname(nickname) do
- redirect(conn, external: OStatus.feed_path(user))
- else
- nil -> {:error, :not_found}
- end
+ with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
+ redirect(conn, external: OStatus.feed_path(user))
end
end
def feed(conn, %{"nickname" => nickname} = params) do
- with %User{} = user <- User.get_cached_by_nickname(nickname) do
+ with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
query_params =
Map.take(params, ["max_id"])
|> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id})
@@ -65,8 +68,6 @@ def feed(conn, %{"nickname" => nickname} = params) do
conn
|> put_resp_content_type("application/atom+xml")
|> send_resp(200, response)
- else
- nil -> {:error, :not_found}
end
end
@@ -97,93 +98,82 @@ def salmon_incoming(conn, _) do
|> send_resp(200, "")
end
- def object(conn, %{"uuid" => uuid}) do
- if get_format(conn) in ["activity+json", "json"] do
- ActivityPubController.call(conn, :object)
- else
- with id <- o_status_url(conn, :object, uuid),
- {_, %Activity{} = activity} <-
- {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
- {_, true} <- {:public?, Visibility.is_public?(activity)},
- %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
- case get_format(conn) do
- "html" -> redirect(conn, to: "/notice/#{activity.id}")
- _ -> represent_activity(conn, nil, activity, user)
- end
- else
- {:public?, false} ->
- {:error, :not_found}
+ def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
+ when format in ["json", "activity+json"] do
+ ActivityPubController.call(conn, :object)
+ end
- {:activity, nil} ->
- {:error, :not_found}
-
- e ->
- e
+ def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
+ with id <- o_status_url(conn, :object, uuid),
+ {_, %Activity{} = activity} <-
+ {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
+ {_, true} <- {:public?, Visibility.is_public?(activity)},
+ %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
+ case format do
+ "html" -> redirect(conn, to: "/notice/#{activity.id}")
+ _ -> represent_activity(conn, nil, activity, user)
end
+ else
+ reason when reason in [{:public?, false}, {:activity, nil}] ->
+ {:error, :not_found}
+
+ e ->
+ e
end
end
- def activity(conn, %{"uuid" => uuid}) do
- if get_format(conn) in ["activity+json", "json"] do
- ActivityPubController.call(conn, :activity)
- else
- with id <- o_status_url(conn, :activity, uuid),
- {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
- {_, true} <- {:public?, Visibility.is_public?(activity)},
- %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
- case format = get_format(conn) do
- "html" -> redirect(conn, to: "/notice/#{activity.id}")
- _ -> represent_activity(conn, format, activity, user)
- end
- else
- {:public?, false} ->
- {:error, :not_found}
+ def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
+ when format in ["json", "activity+json"] do
+ ActivityPubController.call(conn, :activity)
+ end
- {:activity, nil} ->
- {:error, :not_found}
-
- e ->
- e
+ def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
+ with id <- o_status_url(conn, :activity, uuid),
+ {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
+ {_, true} <- {:public?, Visibility.is_public?(activity)},
+ %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
+ case format do
+ "html" -> redirect(conn, to: "/notice/#{activity.id}")
+ _ -> represent_activity(conn, format, activity, user)
end
+ else
+ reason when reason in [{:public?, false}, {:activity, nil}] ->
+ {:error, :not_found}
+
+ e ->
+ e
end
end
- def notice(conn, %{"id" => id}) do
+ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)},
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
- case format = get_format(conn) do
- "html" ->
- if activity.data["type"] == "Create" do
- %Object{} = object = Object.normalize(activity)
+ cond do
+ format == "html" && activity.data["type"] == "Create" ->
+ %Object{} = object = Object.normalize(activity)
- Fallback.RedirectController.redirector_with_meta(conn, %{
+ RedirectController.redirector_with_meta(
+ conn,
+ %{
activity_id: activity.id,
object: object,
- url:
- Pleroma.Web.Router.Helpers.o_status_url(
- Pleroma.Web.Endpoint,
- :notice,
- activity.id
- ),
+ url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
user: user
- })
- else
- Fallback.RedirectController.redirector(conn, nil)
- end
+ }
+ )
- _ ->
+ format == "html" ->
+ RedirectController.redirector(conn, nil)
+
+ true ->
represent_activity(conn, format, activity, user)
end
else
- {:public?, false} ->
+ reason when reason in [{:public?, false}, {:activity, nil}] ->
conn
|> put_status(404)
- |> Fallback.RedirectController.redirector(nil, 404)
-
- {:activity, nil} ->
- conn
- |> Fallback.RedirectController.redirector(nil, 404)
+ |> RedirectController.redirector(nil, 404)
e ->
e
@@ -204,13 +194,13 @@ def notice_player(conn, %{"id" => id}) do
"content-security-policy",
"default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
)
- |> put_view(Pleroma.Web.Metadata.PlayerView)
+ |> put_view(PlayerView)
|> render("player.html", url)
else
_error ->
conn
|> put_status(404)
- |> Fallback.RedirectController.redirector(nil, 404)
+ |> RedirectController.redirector(nil, 404)
end
end
@@ -248,6 +238,8 @@ def errors(conn, {:error, :not_found}) do
render_error(conn, :not_found, "Not found")
end
+ def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
+
def errors(conn, _) do
render_error(conn, :internal_server_error, "Something went wrong")
end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index bb7648bdd..9f756effb 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -101,160 +101,538 @@ test "returns 404 for a missing feed", %{conn: conn} do
assert response(conn, 404)
end
- test "gets an object", %{conn: conn} do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- user = User.get_cached_by_ap_id(note_activity.data["actor"])
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
- url = "/objects/#{uuid}"
+ describe "GET object/2" do
+ test "gets an object", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+ url = "/objects/#{uuid}"
+
+ conn =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get(url)
+
+ expected =
+ ActivityRepresenter.to_simple_form(note_activity, user, true)
+ |> ActivityRepresenter.wrap_with_entry()
+ |> :xmerl.export_simple(:xmerl_xml)
+ |> to_string
+
+ assert response(conn, 200) == expected
+ end
+
+ test "redirects to /notice/id for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+ url = "/objects/#{uuid}"
+
+ conn =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get(url)
+
+ assert redirected_to(conn) == "/notice/#{note_activity.id}"
+ end
+
+ test "500s when user not found", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+ User.invalidate_cache(user)
+ Pleroma.Repo.delete(user)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+ url = "/objects/#{uuid}"
+
+ conn =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get(url)
+
+ assert response(conn, 500) == ~S({"error":"Something went wrong"})
+ end
+
+ test "404s on private objects", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+
+ conn
+ |> get("/objects/#{uuid}")
+ |> response(404)
+ end
+
+ test "404s on nonexisting objects", %{conn: conn} do
+ conn
+ |> get("/objects/123")
+ |> response(404)
+ end
+ end
+
+ describe "GET activity/2" do
+ test "gets an activity in xml format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
- conn =
conn
|> put_req_header("accept", "application/xml")
- |> get(url)
+ |> get("/activities/#{uuid}")
+ |> response(200)
+ end
- expected =
- ActivityRepresenter.to_simple_form(note_activity, user, true)
- |> ActivityRepresenter.wrap_with_entry()
- |> :xmerl.export_simple(:xmerl_xml)
- |> to_string
+ test "redirects to /notice/id for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
- assert response(conn, 200) == expected
+ conn =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/activities/#{uuid}")
+
+ assert redirected_to(conn) == "/notice/#{note_activity.id}"
+ end
+
+ test "505s when user not found", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+ User.invalidate_cache(user)
+ Pleroma.Repo.delete(user)
+
+ conn =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/activities/#{uuid}")
+
+ assert response(conn, 500) == ~S({"error":"Something went wrong"})
+ end
+
+ test "404s on deleted objects", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/objects/#{uuid}")
+ |> response(200)
+
+ Object.delete(object)
+
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/objects/#{uuid}")
+ |> response(404)
+ end
+
+ test "404s on private activities", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
+
+ conn
+ |> get("/activities/#{uuid}")
+ |> response(404)
+ end
+
+ test "404s on nonexistent activities", %{conn: conn} do
+ conn
+ |> get("/activities/123")
+ |> response(404)
+ end
+
+ test "gets an activity in AS2 format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
+ url = "/activities/#{uuid}"
+
+ conn =
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get(url)
+
+ assert json_response(conn, 200)
+ end
end
- test "404s on private objects", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- object = Object.normalize(note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+ describe "GET notice/2" do
+ test "gets a notice in xml format", %{conn: conn} do
+ note_activity = insert(:note_activity)
- conn
- |> get("/objects/#{uuid}")
- |> response(404)
- end
+ conn
+ |> get("/notice/#{note_activity.id}")
+ |> response(200)
+ end
- test "404s on nonexisting objects", %{conn: conn} do
- conn
- |> get("/objects/123")
- |> response(404)
- end
+ test "gets a notice in AS2 format", %{conn: conn} do
+ note_activity = insert(:note_activity)
- test "gets an activity in xml format", %{conn: conn} do
- note_activity = insert(:note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
- conn
- |> put_req_header("accept", "application/xml")
- |> get("/activities/#{uuid}")
- |> response(200)
- end
-
- test "404s on deleted objects", %{conn: conn} do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-
- conn
- |> put_req_header("accept", "application/xml")
- |> get("/objects/#{uuid}")
- |> response(200)
-
- Object.delete(object)
-
- conn
- |> put_req_header("accept", "application/xml")
- |> get("/objects/#{uuid}")
- |> response(404)
- end
-
- test "404s on private activities", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
- conn
- |> get("/activities/#{uuid}")
- |> response(404)
- end
-
- test "404s on nonexistent activities", %{conn: conn} do
- conn
- |> get("/activities/123")
- |> response(404)
- end
-
- test "gets a notice in xml format", %{conn: conn} do
- note_activity = insert(:note_activity)
-
- conn
- |> get("/notice/#{note_activity.id}")
- |> response(200)
- end
-
- test "gets a notice in AS2 format", %{conn: conn} do
- note_activity = insert(:note_activity)
-
- conn
- |> put_req_header("accept", "application/activity+json")
- |> get("/notice/#{note_activity.id}")
- |> json_response(200)
- end
-
- test "only gets a notice in AS2 format for Create messages", %{conn: conn} do
- note_activity = insert(:note_activity)
- url = "/notice/#{note_activity.id}"
-
- conn =
conn
|> put_req_header("accept", "application/activity+json")
- |> get(url)
+ |> get("/notice/#{note_activity.id}")
+ |> json_response(200)
+ end
- assert json_response(conn, 200)
+ test "500s when actor not found", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+ User.invalidate_cache(user)
+ Pleroma.Repo.delete(user)
- user = insert(:user)
+ conn =
+ conn
+ |> get("/notice/#{note_activity.id}")
- {:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
- url = "/notice/#{like_activity.id}"
+ assert response(conn, 500) == ~S({"error":"Something went wrong"})
+ end
- assert like_activity.data["type"] == "Like"
+ test "only gets a notice in AS2 format for Create messages", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ url = "/notice/#{note_activity.id}"
- conn =
- build_conn()
- |> put_req_header("accept", "application/activity+json")
- |> get(url)
+ conn =
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get(url)
- assert response(conn, 404)
+ assert json_response(conn, 200)
+
+ user = insert(:user)
+
+ {:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
+ url = "/notice/#{like_activity.id}"
+
+ assert like_activity.data["type"] == "Like"
+
+ conn =
+ build_conn()
+ |> put_req_header("accept", "application/activity+json")
+ |> get(url)
+
+ assert response(conn, 404)
+ end
+
+ test "render html for redirect for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+
+ resp =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/notice/#{note_activity.id}")
+ |> response(200)
+
+ assert resp =~
+ " "
+
+ user = insert(:user)
+
+ {:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
+
+ assert like_activity.data["type"] == "Like"
+
+ resp =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/notice/#{like_activity.id}")
+ |> response(200)
+
+ assert resp =~ ""
+ end
+
+ test "404s a private notice", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ url = "/notice/#{note_activity.id}"
+
+ conn =
+ conn
+ |> get(url)
+
+ assert response(conn, 404)
+ end
+
+ test "404s a nonexisting notice", %{conn: conn} do
+ url = "/notice/123"
+
+ conn =
+ conn
+ |> get(url)
+
+ assert response(conn, 404)
+ end
end
- test "gets an activity in AS2 format", %{conn: conn} do
- note_activity = insert(:note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
- url = "/activities/#{uuid}"
+ describe "feed_redirect" do
+ test "undefined format. it redirects to feed", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
- conn =
- conn
- |> put_req_header("accept", "application/activity+json")
- |> get(url)
+ response =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/users/#{user.nickname}")
+ |> response(302)
- assert json_response(conn, 200)
+ assert response ==
+ "You are being redirected ."
+ end
+
+ test "undefined format. it returns error when user not found", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/users/jimm")
+ |> response(404)
+
+ assert response == ~S({"error":"Not found"})
+ end
+
+ test "activity+json format. it redirects on actual feed of user", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+ response =
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get("/users/#{user.nickname}")
+ |> json_response(200)
+
+ assert response["endpoints"] == %{
+ "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
+ "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
+ "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
+ "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox"
+ }
+
+ assert response["@context"] == [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ %{"@language" => "und"}
+ ]
+
+ assert Map.take(response, [
+ "followers",
+ "following",
+ "id",
+ "inbox",
+ "manuallyApprovesFollowers",
+ "name",
+ "outbox",
+ "preferredUsername",
+ "summary",
+ "tag",
+ "type",
+ "url"
+ ]) == %{
+ "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
+ "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
+ "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
+ "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
+ "manuallyApprovesFollowers" => false,
+ "name" => user.name,
+ "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
+ "preferredUsername" => user.nickname,
+ "summary" => user.bio,
+ "tag" => [],
+ "type" => "Person",
+ "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
+ }
+ end
+
+ test "activity+json format. it returns error whe use not found", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get("/users/jimm")
+ |> json_response(404)
+
+ assert response == "Not found"
+ end
+
+ test "json format. it redirects on actual feed of user", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+ response =
+ conn
+ |> put_req_header("accept", "application/json")
+ |> get("/users/#{user.nickname}")
+ |> json_response(200)
+
+ assert response["endpoints"] == %{
+ "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",
+ "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",
+ "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token",
+ "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox"
+ }
+
+ assert response["@context"] == [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ %{"@language" => "und"}
+ ]
+
+ assert Map.take(response, [
+ "followers",
+ "following",
+ "id",
+ "inbox",
+ "manuallyApprovesFollowers",
+ "name",
+ "outbox",
+ "preferredUsername",
+ "summary",
+ "tag",
+ "type",
+ "url"
+ ]) == %{
+ "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers",
+ "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following",
+ "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}",
+ "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox",
+ "manuallyApprovesFollowers" => false,
+ "name" => user.name,
+ "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox",
+ "preferredUsername" => user.nickname,
+ "summary" => user.bio,
+ "tag" => [],
+ "type" => "Person",
+ "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
+ }
+ end
+
+ test "json format. it returns error whe use not found", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("accept", "application/json")
+ |> get("/users/jimm")
+ |> json_response(404)
+
+ assert response == "Not found"
+ end
+
+ test "html format. it redirects on actual feed of user", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+
+ response =
+ conn
+ |> get("/users/#{user.nickname}")
+ |> response(200)
+
+ assert response ==
+ Fallback.RedirectController.redirector_with_meta(
+ conn,
+ %{user: user}
+ ).resp_body
+ end
+
+ test "html format. it returns error when user not found", %{conn: conn} do
+ response =
+ conn
+ |> get("/users/jimm")
+ |> json_response(404)
+
+ assert response == %{"error" => "Not found"}
+ end
end
- test "404s a private notice", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- url = "/notice/#{note_activity.id}"
+ describe "GET /notice/:id/embed_player" do
+ test "render embed player", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
- conn =
- conn
- |> get(url)
+ object_data =
+ Map.put(object.data, "attachment", [
+ %{
+ "url" => [
+ %{
+ "href" =>
+ "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+ "mediaType" => "video/mp4",
+ "type" => "Link"
+ }
+ ]
+ }
+ ])
- assert response(conn, 404)
- end
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
- test "404s a nonexisting notice", %{conn: conn} do
- url = "/notice/123"
+ conn =
+ conn
+ |> get("/notice/#{note_activity.id}/embed_player")
- conn =
- conn
- |> get(url)
+ assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"]
- assert response(conn, 404)
+ assert Plug.Conn.get_resp_header(
+ conn,
+ "content-security-policy"
+ ) == [
+ "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
+ ]
+
+ assert response(conn, 200) =~
+ "Your browser does not support video/mp4 playback. "
+ end
+
+ test "404s when activity isn't create", %{conn: conn} do
+ note_activity = insert(:note_activity, data_attrs: %{"type" => "Like"})
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when activity is direct message", %{conn: conn} do
+ note_activity = insert(:note_activity, data_attrs: %{"directMessage" => true})
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when attachment is empty", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
+ object_data = Map.put(object.data, "attachment", [])
+
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when attachment isn't audio or video", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
+
+ object_data =
+ Map.put(object.data, "attachment", [
+ %{
+ "url" => [
+ %{
+ "href" => "https://peertube.moe/static/webseed/480.jpg",
+ "mediaType" => "image/jpg",
+ "type" => "Link"
+ }
+ ]
+ }
+ ])
+
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
end
end
From c0e258cf21395fa2d5338ee238e4fcf4f3b3bf30 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Mon, 29 Jul 2019 16:17:22 +0000
Subject: [PATCH 053/202] Redirect not logged-in users to the MastoFE login
page on private instances
---
CHANGELOG.md | 1 +
lib/pleroma/web/router.ex | 2 +-
.../mastodon_api/mastodon_api_controller_test.exs | 15 +++++++++++++++
3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 48379b757..5416d452e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rich Media: The crawled URL is now spliced into the rich media data.
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
+- Not being able to access the Mastodon FE login page on private instances
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 4e1ab6c33..0689d69fb 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -698,7 +698,7 @@ defmodule Pleroma.Web.Router do
post("/auth/password", MastodonAPIController, :password_reset)
scope [] do
- pipe_through(:oauth_read_or_public)
+ pipe_through(:oauth_read)
get("/web/*path", MastodonAPIController, :index)
end
end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index d7f92fac2..66016c886 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -3154,6 +3154,21 @@ test "redirects not logged-in users to the login page", %{conn: conn, path: path
assert redirected_to(conn) == "/web/login"
end
+ test "redirects not logged-in users to the login page on private instances", %{
+ conn: conn,
+ path: path
+ } do
+ is_public = Pleroma.Config.get([:instance, :public])
+ Pleroma.Config.put([:instance, :public], false)
+
+ conn = get(conn, path)
+
+ assert conn.status == 302
+ assert redirected_to(conn) == "/web/login"
+
+ Pleroma.Config.put([:instance, :public], is_public)
+ end
+
test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
token = insert(:oauth_token)
From 0bee2131ce55ffd702ddc92800499b01b86d3765 Mon Sep 17 00:00:00 2001
From: Eugenij
Date: Mon, 29 Jul 2019 16:17:40 +0000
Subject: [PATCH 054/202] Add `mailerEnabled` to the NodeInfo metadata
---
CHANGELOG.md | 1 +
lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 1 +
2 files changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5416d452e..e77fe4f3d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
- NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
+- NodeInfo: Return `mailerEnabled` in `metadata`
- Mastodon API: Unsubscribe followers when they unfollow a user
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index a1d7fcc7d..54f89e65c 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -165,6 +165,7 @@ def raw_nodeinfo do
},
accountActivationRequired: Config.get([:instance, :account_activation_required], false),
invitesEnabled: Config.get([:instance, :invites_enabled], false),
+ mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
features: features,
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
From 5795a890e9d14a9e51e2613d26620899b2171623 Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Mon, 29 Jul 2019 19:09:58 +0000
Subject: [PATCH 055/202] markdown: clean up html generated by earmark
---
lib/pleroma/web/common_api/utils.ex | 3 +++
test/web/common_api/common_api_utils_test.exs | 10 +++++-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index d80fffa26..6d42ae8ae 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -300,6 +300,9 @@ def format_input(text, "text/markdown", options) do
|> Earmark.as_html!()
|> Formatter.linkify(options)
|> Formatter.html_escape("text/html")
+ |> (fn {text, mentions, tags} ->
+ {String.replace(text, ~r/\r?\n/, ""), mentions, tags}
+ end).()
end
def make_note_data(
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index af320f31f..38b2319ac 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -99,14 +99,14 @@ test "works for bare text/html" do
test "works for bare text/markdown" do
text = "**hello world**"
- expected = "hello world
\n"
+ expected = "hello world
"
{output, [], []} = Utils.format_input(text, "text/markdown")
assert output == expected
text = "**hello world**\n\n*another paragraph*"
- expected = "hello world
\nanother paragraph
\n"
+ expected = "hello world
another paragraph
"
{output, [], []} = Utils.format_input(text, "text/markdown")
@@ -118,7 +118,7 @@ test "works for bare text/markdown" do
by someone
"""
- expected = "cool quote
\n \nby someone
\n"
+ expected = "cool quote
by someone
"
{output, [], []} = Utils.format_input(text, "text/markdown")
@@ -157,11 +157,11 @@ test "works for text/markdown with mentions" do
text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*"
expected =
- "hello world
\nanother hello world
another @user__test and @user__test google.com paragraph
\n"
+ }\" class=\"u-url mention\" href=\"http://foo.com/user__test\">@user__test google.com paragraph
"
{output, _, _} = Utils.format_input(text, "text/markdown")
From 5835069215b880ad261a006cf310da624a82ca4a Mon Sep 17 00:00:00 2001
From: kaniini
Date: Mon, 29 Jul 2019 19:42:26 +0000
Subject: [PATCH 056/202] Revert "Merge branch
'bugfix/clean-up-markdown-rendering' into 'develop'"
This reverts merge request !1504
---
lib/pleroma/web/common_api/utils.ex | 3 ---
test/web/common_api/common_api_utils_test.exs | 10 +++++-----
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 6d42ae8ae..d80fffa26 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -300,9 +300,6 @@ def format_input(text, "text/markdown", options) do
|> Earmark.as_html!()
|> Formatter.linkify(options)
|> Formatter.html_escape("text/html")
- |> (fn {text, mentions, tags} ->
- {String.replace(text, ~r/\r?\n/, ""), mentions, tags}
- end).()
end
def make_note_data(
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index 38b2319ac..af320f31f 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -99,14 +99,14 @@ test "works for bare text/html" do
test "works for bare text/markdown" do
text = "**hello world**"
- expected = "hello world
"
+ expected = "hello world
\n"
{output, [], []} = Utils.format_input(text, "text/markdown")
assert output == expected
text = "**hello world**\n\n*another paragraph*"
- expected = "hello world
another paragraph
"
+ expected = "hello world
\nanother paragraph
\n"
{output, [], []} = Utils.format_input(text, "text/markdown")
@@ -118,7 +118,7 @@ test "works for bare text/markdown" do
by someone
"""
- expected = "cool quote
by someone
"
+ expected = "cool quote
\n \nby someone
\n"
{output, [], []} = Utils.format_input(text, "text/markdown")
@@ -157,11 +157,11 @@ test "works for text/markdown with mentions" do
text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*"
expected =
- "hello world
another hello world
\nanother @user__test and @user__test google.com paragraph
"
+ }\" class=\"u-url mention\" href=\"http://foo.com/user__test\">@user__test google.com paragraph\n"
{output, _, _} = Utils.format_input(text, "text/markdown")
From 3850812503ebfe0e1eaf84a4067e11e052a8206e Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Mon, 29 Jul 2019 20:00:57 +0000
Subject: [PATCH 057/202] twitter api: utils: rework do_remote_follow() to use
CommonAPI
Closes #1138
---
lib/pleroma/web/twitter_api/controllers/util_controller.ex | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 39bc6147c..5c73a615d 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -15,7 +15,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Plugs.AuthenticationPlug
alias Pleroma.User
alias Pleroma.Web
- alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.WebFinger
@@ -100,8 +99,7 @@ def do_remote_follow(conn, %{
with %User{} = user <- User.get_cached_by_nickname(username),
true <- AuthenticationPlug.checkpw(password, user.password_hash),
%User{} = _followed <- User.get_cached_by_id(id),
- {:ok, follower} <- User.follow(user, followee),
- {:ok, _activity} <- ActivityPub.follow(follower, followee) do
+ {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn
|> render("followed.html", %{error: false})
else
@@ -122,8 +120,7 @@ def do_remote_follow(conn, %{
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
with %User{} = followee <- User.get_cached_by_id(id),
- {:ok, follower} <- User.follow(user, followee),
- {:ok, _activity} <- ActivityPub.follow(follower, followee) do
+ {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn
|> render("followed.html", %{error: false})
else
From 51b3b6d8164de9196159dc7de8d5abf0c4fa1bce Mon Sep 17 00:00:00 2001
From: Alexander Strizhakov
Date: Tue, 30 Jul 2019 16:36:05 +0000
Subject: [PATCH 058/202] Admin changes
---
CHANGELOG.md | 1 +
docs/api/admin_api.md | 23 ++++++++++++
lib/mix/tasks/pleroma/config.ex | 2 +-
.../web/admin_api/admin_api_controller.ex | 10 +++++
lib/pleroma/web/router.ex | 2 +
.../admin_api/admin_api_controller_test.exs | 37 +++++++++++++++++++
6 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e77fe4f3d..acd55362d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
- Admin API: Added support for `tuples`.
+- Admin API: Added endpoints to run mix tasks pleroma.config migrate_to_db & pleroma.config migrate_from_db
- Added synchronization of following/followers counters for external users
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md
index ca9303227..22873dde9 100644
--- a/docs/api/admin_api.md
+++ b/docs/api/admin_api.md
@@ -575,6 +575,29 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- 404 Not Found `"Not found"`
- On success: 200 OK `{}`
+
+## `/api/pleroma/admin/config/migrate_to_db`
+### Run mix task pleroma.config migrate_to_db
+Copy settings on key `:pleroma` to DB.
+- Method `GET`
+- Params: none
+- Response:
+
+```json
+{}
+```
+
+## `/api/pleroma/admin/config/migrate_from_db`
+### Run mix task pleroma.config migrate_from_db
+Copy all settings from DB to `config/prod.exported_from_db.secret.exs` with deletion from DB.
+- Method `GET`
+- Params: none
+- Response:
+
+```json
+{}
+```
+
## `/api/pleroma/admin/config`
### List config settings
List config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex
index a7d0fac5d..462940e7e 100644
--- a/lib/mix/tasks/pleroma/config.ex
+++ b/lib/mix/tasks/pleroma/config.ex
@@ -15,7 +15,7 @@ defmodule Mix.Tasks.Pleroma.Config do
mix pleroma.config migrate_to_db
- ## Transfers config from DB to file.
+ ## Transfers config from DB to file `config/env.exported_from_db.secret.exs`
mix pleroma.config migrate_from_db ENV
"""
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 1ae5acd91..fcda57b3e 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -379,6 +379,16 @@ def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
end
end
+ def migrate_to_db(conn, _params) do
+ Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
+ json(conn, %{})
+ end
+
+ def migrate_from_db(conn, _params) do
+ Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
+ json(conn, %{})
+ end
+
def config_show(conn, _params) do
configs = Pleroma.Repo.all(Config)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 0689d69fb..d475fc973 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -196,6 +196,8 @@ defmodule Pleroma.Web.Router do
get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update)
+ get("/config/migrate_to_db", AdminAPIController, :migrate_to_db)
+ get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
end
scope "/", Pleroma.Web.TwitterAPI do
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 6dda4ae51..824ad23e6 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1916,6 +1916,43 @@ test "queues key as atom", %{conn: conn} do
end
end
+ describe "config mix tasks run" do
+ setup %{conn: conn} do
+ admin = insert(:user, info: %{is_admin: true})
+
+ temp_file = "config/test.exported_from_db.secret.exs"
+
+ on_exit(fn ->
+ :ok = File.rm(temp_file)
+ end)
+
+ dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
+
+ Pleroma.Config.put([:instance, :dynamic_configuration], true)
+
+ on_exit(fn ->
+ Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
+ end)
+
+ %{conn: assign(conn, :user, admin), admin: admin}
+ end
+
+ test "transfer settings to DB and to file", %{conn: conn, admin: admin} do
+ assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
+ conn = get(conn, "/api/pleroma/admin/config/migrate_to_db")
+ assert json_response(conn, 200) == %{}
+ assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) > 0
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/config/migrate_from_db")
+
+ assert json_response(conn, 200) == %{}
+ assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
+ end
+ end
+
describe "GET /api/pleroma/admin/users/:nickname/statuses" do
setup do
admin = insert(:user, info: %{is_admin: true})
From f42719506c539a4058c52d3a6e4a828948ac74ce Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Wed, 31 Jul 2019 14:20:34 +0300
Subject: [PATCH 059/202] Fix credo issues
---
lib/pleroma/user.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index fd1c0a544..7acf1e53c 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -741,7 +741,7 @@ def fetch_follow_information(user) do
end
def update_follower_count(%User{} = user) do
- unless user.local == false and Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ unless !user.local and Pleroma.Config.get([:instance, :external_user_synchronization]) do
follower_count_query =
User.Query.build(%{followers: user, deactivated: false})
|> select([u], %{count: count(u.id)})
From 7483679a7b6ff63c9c61c3df3e9e37f2c24012ff Mon Sep 17 00:00:00 2001
From: lain
Date: Wed, 31 Jul 2019 15:12:29 +0200
Subject: [PATCH 060/202] StatusView: Return direct conversation id.
---
lib/pleroma/conversation/participation.ex | 8 ++++++++
.../web/mastodon_api/views/status_view.ex | 18 +++++++++++++++++-
test/web/mastodon_api/status_view_test.exs | 18 +++++++++++++++++-
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 5883e4183..77b3f61e9 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -65,6 +65,14 @@ def for_user(user, params \\ %{}) do
|> Pleroma.Pagination.fetch_paginated(params)
end
+ def for_user_and_conversation(user, conversation) do
+ from(p in __MODULE__,
+ where: p.user_id == ^user.id,
+ where: p.conversation_id == ^conversation.id
+ )
+ |> Repo.one()
+ end
+
def for_user_with_last_activity_id(user, params \\ %{}) do
for_user(user, params)
|> Enum.map(fn participation ->
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 80df9b2ac..a862554b1 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -6,6 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
use Pleroma.Web, :view
alias Pleroma.Activity
+ alias Pleroma.Conversation
+ alias Pleroma.Conversation.Participation
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Repo
@@ -225,6 +227,19 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
object.data["url"] || object.data["external_url"] || object.data["id"]
end
+ direct_conversation_id =
+ with {_, true} <- {:include_id, opts[:with_direct_conversation_id]},
+ {_, %User{} = for_user} <- {:for_user, opts[:for]},
+ %{data: %{"context" => context}} when is_binary(context) <- activity,
+ %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
+ %Participation{id: participation_id} <-
+ Participation.for_user_and_conversation(for_user, conversation) do
+ participation_id
+ else
+ _e ->
+ nil
+ end
+
%{
id: to_string(activity.id),
uri: object.data["id"],
@@ -262,7 +277,8 @@ def render("status.json", %{activity: %{data: %{"object" => _object}} = activity
conversation_id: get_context_id(activity),
in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
content: %{"text/plain" => content_plaintext},
- spoiler_text: %{"text/plain" => summary_plaintext}
+ spoiler_text: %{"text/plain" => summary_plaintext},
+ direct_conversation_id: direct_conversation_id
}
}
end
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index 0b167f839..c983b494f 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -23,6 +23,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
:ok
end
+ test "returns the direct conversation id when given the `with_conversation_id` option" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
+
+ status =
+ StatusView.render("status.json",
+ activity: activity,
+ with_direct_conversation_id: true,
+ for: user
+ )
+
+ assert status[:pleroma][:direct_conversation_id]
+ end
+
test "returns a temporary ap_id based user for activities missing db users" do
user = insert(:user)
@@ -133,7 +148,8 @@ test "a note activity" do
conversation_id: convo_id,
in_reply_to_account_acct: nil,
content: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["content"])},
- spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])}
+ spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(object_data["summary"])},
+ direct_conversation_id: nil
}
}
From 58443d0cd683c227199eb34d660191292e487a14 Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 31 Jul 2019 15:14:36 +0000
Subject: [PATCH 061/202] tests for TwitterApi/UtilController
---
lib/pleroma/user.ex | 2 +
.../controllers/util_controller.ex | 186 ++++----
test/support/http_request_mock.ex | 4 +
test/web/twitter_api/util_controller_test.exs | 404 +++++++++++++++++-
4 files changed, 503 insertions(+), 93 deletions(-)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 6e2fd3af8..1adb82f32 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -226,6 +226,7 @@ def password_update_changeset(struct, params) do
|> put_password_hash
end
+ @spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def reset_password(%User{id: user_id} = user, data) do
multi =
Multi.new()
@@ -330,6 +331,7 @@ def needs_update?(%User{local: false} = user) do
def needs_update?(_), do: true
+ @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
{:ok, follower}
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 5c73a615d..3405bd3b7 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -18,6 +18,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.WebFinger
+ plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
+
def help_test(conn, _params) do
json(conn, "ok")
end
@@ -58,27 +60,25 @@ def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
%Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
redirect(conn, to: "/notice/#{activity_id}")
else
- {err, followee} = User.get_or_fetch(acct)
- avatar = User.avatar_url(followee)
- name = followee.nickname
- id = followee.id
-
- if !!user do
+ with {:ok, followee} <- User.get_or_fetch(acct) do
conn
- |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
- else
- conn
- |> render("follow_login.html", %{
+ |> render(follow_template(user), %{
error: false,
acct: acct,
- avatar: avatar,
- name: name,
- id: id
+ avatar: User.avatar_url(followee),
+ name: followee.nickname,
+ id: followee.id
})
+ else
+ {:error, _reason} ->
+ render(conn, follow_template(user), %{error: :error})
end
end
end
+ defp follow_template(%User{} = _user), do: "follow.html"
+ defp follow_template(_), do: "follow_login.html"
+
defp is_status?(acct) do
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
{:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
@@ -92,48 +92,53 @@ defp is_status?(acct) do
def do_remote_follow(conn, %{
"authorization" => %{"name" => username, "password" => password, "id" => id}
}) do
- followee = User.get_cached_by_id(id)
- avatar = User.avatar_url(followee)
- name = followee.nickname
-
- with %User{} = user <- User.get_cached_by_nickname(username),
- true <- AuthenticationPlug.checkpw(password, user.password_hash),
- %User{} = _followed <- User.get_cached_by_id(id),
+ with %User{} = followee <- User.get_cached_by_id(id),
+ {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee},
+ {_, true, _} <- {
+ :auth,
+ AuthenticationPlug.checkpw(password, user.password_hash),
+ followee
+ },
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn
|> render("followed.html", %{error: false})
else
# Was already following user
{:error, "Could not follow user:" <> _rest} ->
- render(conn, "followed.html", %{error: false})
+ render(conn, "followed.html", %{error: "Error following account"})
- _e ->
+ {:auth, _, followee} ->
conn
|> render("follow_login.html", %{
error: "Wrong username or password",
id: id,
- name: name,
- avatar: avatar
+ name: followee.nickname,
+ avatar: User.avatar_url(followee)
})
+
+ e ->
+ Logger.debug("Remote follow failed with error #{inspect(e)}")
+ render(conn, "followed.html", %{error: "Something went wrong."})
end
end
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
- with %User{} = followee <- User.get_cached_by_id(id),
+ with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do
conn
|> render("followed.html", %{error: false})
else
# Was already following user
{:error, "Could not follow user:" <> _rest} ->
- conn
- |> render("followed.html", %{error: false})
+ render(conn, "followed.html", %{error: "Error following account"})
+
+ {:fetch_user, error} ->
+ Logger.debug("Remote follow failed with error #{inspect(error)}")
+ render(conn, "followed.html", %{error: "Could not find user"})
e ->
Logger.debug("Remote follow failed with error #{inspect(e)}")
-
- conn
- |> render("followed.html", %{error: inspect(e)})
+ render(conn, "followed.html", %{error: "Something went wrong."})
end
end
@@ -148,67 +153,70 @@ def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_
end
end
+ def config(%{assigns: %{format: "xml"}} = conn, _params) do
+ instance = Pleroma.Config.get(:instance)
+
+ response = """
+
+
+ #{Keyword.get(instance, :name)}
+ #{Web.base_url()}
+ #{Keyword.get(instance, :limit)}
+ #{!Keyword.get(instance, :registrations_open)}
+
+
+ """
+
+ conn
+ |> put_resp_content_type("application/xml")
+ |> send_resp(200, response)
+ end
+
def config(conn, _params) do
instance = Pleroma.Config.get(:instance)
- case get_format(conn) do
- "xml" ->
- response = """
-
-
- #{Keyword.get(instance, :name)}
- #{Web.base_url()}
- #{Keyword.get(instance, :limit)}
- #{!Keyword.get(instance, :registrations_open)}
-
-
- """
+ vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
- conn
- |> put_resp_content_type("application/xml")
- |> send_resp(200, response)
+ uploadlimit = %{
+ uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
+ avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
+ backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
+ bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
+ }
- _ ->
- vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
+ data = %{
+ name: Keyword.get(instance, :name),
+ description: Keyword.get(instance, :description),
+ server: Web.base_url(),
+ textlimit: to_string(Keyword.get(instance, :limit)),
+ uploadlimit: uploadlimit,
+ closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
+ private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
+ vapidPublicKey: vapid_public_key,
+ accountActivationRequired:
+ bool_to_val(Keyword.get(instance, :account_activation_required, false)),
+ invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
+ safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
+ }
- uploadlimit = %{
- uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
- avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
- backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
- bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
- }
-
- data = %{
- name: Keyword.get(instance, :name),
- description: Keyword.get(instance, :description),
- server: Web.base_url(),
- textlimit: to_string(Keyword.get(instance, :limit)),
- uploadlimit: uploadlimit,
- closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
- private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
- vapidPublicKey: vapid_public_key,
- accountActivationRequired:
- if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
- invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
- safeDMMentionsEnabled:
- if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
- }
+ managed_config = Keyword.get(instance, :managed_config)
+ data =
+ if managed_config do
pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
+ Map.put(data, "pleromafe", pleroma_fe)
+ else
+ data
+ end
- managed_config = Keyword.get(instance, :managed_config)
-
- data =
- if managed_config do
- data |> Map.put("pleromafe", pleroma_fe)
- else
- data
- end
-
- json(conn, %{site: data})
- end
+ json(conn, %{site: data})
end
+ defp bool_to_val(true), do: "1"
+ defp bool_to_val(_), do: "0"
+ defp bool_to_val(true, val, _), do: val
+ defp bool_to_val(_, _, val), do: val
+
def frontend_configurations(conn, _params) do
config =
Pleroma.Config.get(:frontend_configurations, %{})
@@ -217,20 +225,16 @@ def frontend_configurations(conn, _params) do
json(conn, config)
end
- def version(conn, _params) do
+ def version(%{assigns: %{format: "xml"}} = conn, _params) do
version = Pleroma.Application.named_version()
- case get_format(conn) do
- "xml" ->
- response = "#{version} "
+ conn
+ |> put_resp_content_type("application/xml")
+ |> send_resp(200, "#{version} ")
+ end
- conn
- |> put_resp_content_type("application/xml")
- |> send_resp(200, response)
-
- _ ->
- json(conn, version)
- end
+ def version(conn, _params) do
+ json(conn, Pleroma.Application.named_version())
end
def emoji(conn, _params) do
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 2ed5f5042..d767ab9d4 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -51,6 +51,10 @@ def get("https://mastodon.social/users/emelie", _, _, _) do
}}
end
+ def get("https://mastodon.social/users/not_found", _, _, _) do
+ {:ok, %Tesla.Env{status: 404}}
+ end
+
def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do
{:ok,
%Tesla.Env{
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
index 3d699e1df..640579c09 100644
--- a/test/web/twitter_api/util_controller_test.exs
+++ b/test/web/twitter_api/util_controller_test.exs
@@ -14,6 +14,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
setup do
Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ instance_config = Pleroma.Config.get([:instance])
+ pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
+ deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
+
+ on_exit(fn ->
+ Pleroma.Config.put([:instance], instance_config)
+ Pleroma.Config.put([:frontend_configurations, :pleroma_fe], pleroma_fe)
+ Pleroma.Config.put([:user, :deny_follow_blocked], deny_follow_blocked)
+ end)
+
:ok
end
@@ -31,6 +42,35 @@ test "it returns HTTP 200", %{conn: conn} do
assert response == "job started"
end
+ test "it imports follow lists from file", %{conn: conn} do
+ user1 = insert(:user)
+ user2 = insert(:user)
+
+ with_mocks([
+ {File, [],
+ read!: fn "follow_list.txt" ->
+ "Account address,Show boosts\n#{user2.ap_id},true"
+ end},
+ {PleromaJobQueue, [:passthrough], []}
+ ]) do
+ response =
+ conn
+ |> assign(:user, user1)
+ |> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}})
+ |> json_response(:ok)
+
+ assert called(
+ PleromaJobQueue.enqueue(
+ :background,
+ User,
+ [:follow_import, user1, [user2.ap_id]]
+ )
+ )
+
+ assert response == "job started"
+ end
+ end
+
test "it imports new-style mastodon follow lists", %{conn: conn} do
user1 = insert(:user)
user2 = insert(:user)
@@ -79,6 +119,33 @@ test "it returns HTTP 200", %{conn: conn} do
assert response == "job started"
end
+
+ test "it imports blocks users from file", %{conn: conn} do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ user3 = insert(:user)
+
+ with_mocks([
+ {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end},
+ {PleromaJobQueue, [:passthrough], []}
+ ]) do
+ response =
+ conn
+ |> assign(:user, user1)
+ |> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}})
+ |> json_response(:ok)
+
+ assert called(
+ PleromaJobQueue.enqueue(
+ :background,
+ User,
+ [:blocks_import, user1, [user2.ap_id, user3.ap_id]]
+ )
+ )
+
+ assert response == "job started"
+ end
+ end
end
describe "POST /api/pleroma/notifications/read" do
@@ -98,6 +165,18 @@ test "it marks a single notification as read", %{conn: conn} do
assert Repo.get(Notification, notification1.id).seen
refute Repo.get(Notification, notification2.id).seen
end
+
+ test "it returns error when notification not found", %{conn: conn} do
+ user1 = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user1)
+ |> post("/api/pleroma/notifications/read", %{"id" => "22222222222222"})
+ |> json_response(403)
+
+ assert response == %{"error" => "Cannot get notification"}
+ end
end
describe "PUT /api/pleroma/notification_settings" do
@@ -123,7 +202,63 @@ test "it updates notification settings", %{conn: conn} do
end
end
- describe "GET /api/statusnet/config.json" do
+ describe "GET /api/statusnet/config" do
+ test "it returns config in xml format", %{conn: conn} do
+ instance = Pleroma.Config.get(:instance)
+
+ response =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/api/statusnet/config")
+ |> response(:ok)
+
+ assert response ==
+ "\n\n#{Keyword.get(instance, :name)} \n#{
+ Pleroma.Web.base_url()
+ } \n#{Keyword.get(instance, :limit)} \n#{
+ !Keyword.get(instance, :registrations_open)
+ } \n \n \n"
+ end
+
+ test "it returns config in json format", %{conn: conn} do
+ instance = Pleroma.Config.get(:instance)
+ Pleroma.Config.put([:instance, :managed_config], true)
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ Pleroma.Config.put([:instance, :invites_enabled], true)
+ Pleroma.Config.put([:instance, :public], false)
+ Pleroma.Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"})
+
+ response =
+ conn
+ |> put_req_header("accept", "application/json")
+ |> get("/api/statusnet/config")
+ |> json_response(:ok)
+
+ expected_data = %{
+ "site" => %{
+ "accountActivationRequired" => "0",
+ "closed" => "1",
+ "description" => Keyword.get(instance, :description),
+ "invitesEnabled" => "1",
+ "name" => Keyword.get(instance, :name),
+ "pleromafe" => %{"theme" => "asuka-hospital"},
+ "private" => "1",
+ "safeDMMentionsEnabled" => "0",
+ "server" => Pleroma.Web.base_url(),
+ "textlimit" => to_string(Keyword.get(instance, :limit)),
+ "uploadlimit" => %{
+ "avatarlimit" => to_string(Keyword.get(instance, :avatar_upload_limit)),
+ "backgroundlimit" => to_string(Keyword.get(instance, :background_upload_limit)),
+ "bannerlimit" => to_string(Keyword.get(instance, :banner_upload_limit)),
+ "uploadlimit" => to_string(Keyword.get(instance, :upload_limit))
+ },
+ "vapidPublicKey" => Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
+ }
+ }
+
+ assert response == expected_data
+ end
+
test "returns the state of safe_dm_mentions flag", %{conn: conn} do
option = Pleroma.Config.get([:instance, :safe_dm_mentions])
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
@@ -210,7 +345,7 @@ test "returns json with custom emoji with tags", %{conn: conn} do
end
end
- describe "GET /ostatus_subscribe?acct=...." do
+ describe "GET /ostatus_subscribe - remote_follow/2" do
test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
conn =
get(
@@ -230,6 +365,172 @@ test "show follow account page if the `acct` is a account link", %{conn: conn} d
assert html_response(response, 200) =~ "Log in to follow"
end
+
+ test "show follow page if the `acct` is a account link", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> get("/ostatus_subscribe?acct=https://mastodon.social/users/emelie")
+
+ assert html_response(response, 200) =~ "Remote follow"
+ end
+
+ test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> get("/ostatus_subscribe?acct=https://mastodon.social/users/not_found")
+
+ assert html_response(response, 200) =~ "Error fetching user"
+ end
+ end
+
+ describe "POST /ostatus_subscribe - do_remote_follow/2 with assigned user " do
+ test "follows user", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Account followed!"
+ assert user2.follower_address in refresh_record(user).following
+ end
+
+ test "returns error when user is deactivated", %{conn: conn} do
+ user = insert(:user, info: %{deactivated: true})
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when user is blocked", %{conn: conn} do
+ Pleroma.Config.put([:user, :deny_follow_blocked], true)
+ user = insert(:user)
+ user2 = insert(:user)
+
+ {:ok, _user} = Pleroma.User.block(user2, user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when followee not found", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/ostatus_subscribe", %{"user" => %{"id" => "jimm"}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns success result when user already in followers", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+ {:ok, _, _, _} = CommonAPI.follow(user, user2)
+
+ response =
+ conn
+ |> assign(:user, refresh_record(user))
+ |> post("/ostatus_subscribe", %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Account followed!"
+ end
+ end
+
+ describe "POST /ostatus_subscribe - do_remote_follow/2 without assigned user " do
+ test "follows", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> post("/ostatus_subscribe", %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Account followed!"
+ assert user2.follower_address in refresh_record(user).following
+ end
+
+ test "returns error when followee not found", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post("/ostatus_subscribe", %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"}
+ })
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when login invalid", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post("/ostatus_subscribe", %{
+ "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id}
+ })
+ |> response(200)
+
+ assert response =~ "Wrong username or password"
+ end
+
+ test "returns error when password invalid", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> post("/ostatus_subscribe", %{
+ "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Wrong username or password"
+ end
+
+ test "returns error when user is blocked", %{conn: conn} do
+ Pleroma.Config.put([:user, :deny_follow_blocked], true)
+ user = insert(:user)
+ user2 = insert(:user)
+ {:ok, _user} = Pleroma.User.block(user2, user)
+
+ response =
+ conn
+ |> post("/ostatus_subscribe", %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
end
describe "GET /api/pleroma/healthcheck" do
@@ -311,5 +612,104 @@ test "it returns HTTP 200", %{conn: conn} do
assert user.info.deactivated == true
end
+
+ test "it returns returns when password invalid", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/api/pleroma/disable_account", %{"password" => "test1"})
+ |> json_response(:ok)
+
+ assert response == %{"error" => "Invalid password."}
+ user = User.get_cached_by_id(user.id)
+
+ refute user.info.deactivated
+ end
+ end
+
+ describe "GET /api/statusnet/version" do
+ test "it returns version in xml format", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("accept", "application/xml")
+ |> get("/api/statusnet/version")
+ |> response(:ok)
+
+ assert response == "#{Pleroma.Application.named_version()} "
+ end
+
+ test "it returns version in json format", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("accept", "application/json")
+ |> get("/api/statusnet/version")
+ |> json_response(:ok)
+
+ assert response == "#{Pleroma.Application.named_version()}"
+ end
+ end
+
+ describe "POST /main/ostatus - remote_subscribe/2" do
+ test "renders subscribe form", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""})
+ |> response(:ok)
+
+ refute response =~ "Could not find user"
+ assert response =~ "Remotely follow #{user.nickname}"
+ end
+
+ test "renders subscribe form with error when user not found", %{conn: conn} do
+ response =
+ conn
+ |> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""})
+ |> response(:ok)
+
+ assert response =~ "Could not find user"
+ refute response =~ "Remotely follow"
+ end
+
+ test "it redirect to webfinger url", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user, ap_id: "shp@social.heldscal.la")
+
+ conn =
+ conn
+ |> post("/main/ostatus", %{
+ "user" => %{"nickname" => user.nickname, "profile" => user2.ap_id}
+ })
+
+ assert redirected_to(conn) ==
+ "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}"
+ end
+
+ test "it renders form with error when use not found", %{conn: conn} do
+ user2 = insert(:user, ap_id: "shp@social.heldscal.la")
+
+ response =
+ conn
+ |> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}})
+ |> response(:ok)
+
+ assert response =~ "Something went wrong."
+ end
+ end
+
+ test "it returns new captcha", %{conn: conn} do
+ with_mock Pleroma.Captcha,
+ new: fn -> "test_captcha" end do
+ resp =
+ conn
+ |> get("/api/pleroma/captcha")
+ |> response(200)
+
+ assert resp == "\"test_captcha\""
+ assert called(Pleroma.Captcha.new())
+ end
end
end
From 301ea0dc0466371032f44f3e936d1b951ed9784c Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Wed, 31 Jul 2019 19:37:55 +0300
Subject: [PATCH 062/202] Add tests for counters being updated on follow
---
lib/pleroma/user.ex | 2 +-
.../masto_closed_followers_page.json | 1 +
.../masto_closed_following_page.json | 1 +
test/support/http_request_mock.ex | 16 ++++
test/user_test.exs | 74 +++++++++++++++++++
test/web/activity_pub/activity_pub_test.exs | 20 -----
6 files changed, 93 insertions(+), 21 deletions(-)
create mode 100644 test/fixtures/users_mock/masto_closed_followers_page.json
create mode 100644 test/fixtures/users_mock/masto_closed_following_page.json
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 7acf1e53c..69835f3dd 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -741,7 +741,7 @@ def fetch_follow_information(user) do
end
def update_follower_count(%User{} = user) do
- unless !user.local and Pleroma.Config.get([:instance, :external_user_synchronization]) do
+ if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
follower_count_query =
User.Query.build(%{followers: user, deactivated: false})
|> select([u], %{count: count(u.id)})
diff --git a/test/fixtures/users_mock/masto_closed_followers_page.json b/test/fixtures/users_mock/masto_closed_followers_page.json
new file mode 100644
index 000000000..04ab0c4d3
--- /dev/null
+++ b/test/fixtures/users_mock/masto_closed_followers_page.json
@@ -0,0 +1 @@
+{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/followers?page=1","type":"OrderedCollectionPage","totalItems":437,"next":"http://localhost:4001/users/masto_closed/followers?page=2","partOf":"http://localhost:4001/users/masto_closed/followers","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}
diff --git a/test/fixtures/users_mock/masto_closed_following_page.json b/test/fixtures/users_mock/masto_closed_following_page.json
new file mode 100644
index 000000000..8d8324699
--- /dev/null
+++ b/test/fixtures/users_mock/masto_closed_following_page.json
@@ -0,0 +1 @@
+{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:4001/users/masto_closed/following?page=1","type":"OrderedCollectionPage","totalItems":152,"next":"http://localhost:4001/users/masto_closed/following?page=2","partOf":"http://localhost:4001/users/masto_closed/following","orderedItems":["https://testing.uguu.ltd/users/rin","https://patch.cx/users/rin","https://letsalllovela.in/users/xoxo","https://pleroma.site/users/crushv","https://aria.company/users/boris","https://kawen.space/users/crushv","https://freespeech.host/users/cvcvcv","https://pleroma.site/users/picpub","https://pixelfed.social/users/nosleep","https://boopsnoot.gq/users/5c1896d162f7d337f90492a3","https://pikachu.rocks/users/waifu","https://royal.crablettesare.life/users/crablettes"]}
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 2ed5f5042..bdfe43b28 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -792,6 +792,14 @@ def get("http://localhost:4001/users/masto_closed/followers", _, _, _) do
}}
end
+ def get("http://localhost:4001/users/masto_closed/followers?page=1", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json")
+ }}
+ end
+
def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
{:ok,
%Tesla.Env{
@@ -800,6 +808,14 @@ def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
}}
end
+ def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json")
+ }}
+ end
+
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
{:ok,
%Tesla.Env{
diff --git a/test/user_test.exs b/test/user_test.exs
index 556df45fd..7ec241c25 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -1393,4 +1393,78 @@ test "performs update cache if user updated" do
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
end
end
+
+ describe "following/followers synchronization" do
+ setup do
+ sync = Pleroma.Config.get([:instance, :external_user_synchronization])
+ on_exit(fn -> Pleroma.Config.put([:instance, :external_user_synchronization], sync) end)
+ end
+
+ test "updates the counters normally on following/getting a follow when disabled" do
+ Pleroma.Config.put([:instance, :external_user_synchronization], false)
+ user = insert(:user)
+
+ other_user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following",
+ info: %{ap_enabled: true}
+ )
+
+ assert User.user_info(other_user).following_count == 0
+ assert User.user_info(other_user).follower_count == 0
+
+ {:ok, user} = Pleroma.User.follow(user, other_user)
+ other_user = Pleroma.User.get_by_id(other_user.id)
+
+ assert User.user_info(user).following_count == 1
+ assert User.user_info(other_user).follower_count == 1
+ end
+
+ test "syncronizes the counters with the remote instance for the followed when enabled" do
+ Pleroma.Config.put([:instance, :external_user_synchronization], false)
+
+ user = insert(:user)
+
+ other_user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following",
+ info: %{ap_enabled: true}
+ )
+
+ assert User.user_info(other_user).following_count == 0
+ assert User.user_info(other_user).follower_count == 0
+
+ Pleroma.Config.put([:instance, :external_user_synchronization], true)
+ {:ok, _user} = User.follow(user, other_user)
+ other_user = User.get_by_id(other_user.id)
+
+ assert User.user_info(other_user).follower_count == 437
+ end
+
+ test "syncronizes the counters with the remote instance for the follower when enabled" do
+ Pleroma.Config.put([:instance, :external_user_synchronization], false)
+
+ user = insert(:user)
+
+ other_user =
+ insert(:user,
+ local: false,
+ follower_address: "http://localhost:4001/users/masto_closed/followers",
+ following_address: "http://localhost:4001/users/masto_closed/following",
+ info: %{ap_enabled: true}
+ )
+
+ assert User.user_info(other_user).following_count == 0
+ assert User.user_info(other_user).follower_count == 0
+
+ Pleroma.Config.put([:instance, :external_user_synchronization], true)
+ {:ok, other_user} = User.follow(other_user, user)
+
+ assert User.user_info(other_user).following_count == 152
+ end
+ end
end
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 853c93ab5..3d9a678dd 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -1149,16 +1149,6 @@ test "detects hidden followers" do
"http://localhost:4001/users/masto_closed/followers?page=1" ->
%Tesla.Env{status: 403, body: ""}
- "http://localhost:4001/users/masto_closed/following?page=1" ->
- %Tesla.Env{
- status: 200,
- body:
- Jason.encode!(%{
- "id" => "http://localhost:4001/users/masto_closed/following?page=1",
- "type" => "OrderedCollectionPage"
- })
- }
-
_ ->
apply(HttpRequestMock, :request, [env])
end
@@ -1182,16 +1172,6 @@ test "detects hidden follows" do
"http://localhost:4001/users/masto_closed/following?page=1" ->
%Tesla.Env{status: 403, body: ""}
- "http://localhost:4001/users/masto_closed/followers?page=1" ->
- %Tesla.Env{
- status: 200,
- body:
- Jason.encode!(%{
- "id" => "http://localhost:4001/users/masto_closed/followers?page=1",
- "type" => "OrderedCollectionPage"
- })
- }
-
_ ->
apply(HttpRequestMock, :request, [env])
end
From f72e0b7caddd96da67269552db3102733e4a2581 Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Wed, 31 Jul 2019 17:23:16 +0000
Subject: [PATCH 063/202] ostatus: explicitly disallow protocol downgrade from
activitypub
This closes embargoed bug #1135.
---
CHANGELOG.md | 3 ++
.../web/ostatus/handlers/follow_handler.ex | 2 +-
.../web/ostatus/handlers/note_handler.ex | 2 +-
.../web/ostatus/handlers/unfollow_handler.ex | 2 +-
lib/pleroma/web/ostatus/ostatus.ex | 17 +++++--
test/web/ostatus/ostatus_test.exs | 48 +++++++++++++++++--
6 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index acd55362d..b02ed243b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
+### Security
+- OStatus: eliminate the possibility of a protocol downgrade attack.
+
### Changed
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
index 263d3b2dc..03e4cbbb0 100644
--- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.OStatus.FollowHandler do
alias Pleroma.Web.XML
def handle(entry, doc) do
- with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
+ with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
followed_uri when not is_nil(followed_uri) <-
XML.string_from_xpath("/entry/activity:object/id", entry),
diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex
index 3005e8f57..7fae14f7b 100644
--- a/lib/pleroma/web/ostatus/handlers/note_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex
@@ -111,7 +111,7 @@ def handle_note(entry, doc \\ nil, options \\ []) do
with id <- XML.string_from_xpath("//id", entry),
activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),
[author] <- :xmerl_xpath.string('//author[1]', doc),
- {:ok, actor} <- OStatus.find_make_or_update_user(author),
+ {:ok, actor} <- OStatus.find_make_or_update_actor(author),
content_html <- OStatus.get_content(entry),
cw <- OStatus.get_cw(entry),
in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry),
diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
index 6596ada3b..2062432e3 100644
--- a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.OStatus.UnfollowHandler do
alias Pleroma.Web.XML
def handle(entry, doc) do
- with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
+ with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),
followed_uri when not is_nil(followed_uri) <-
XML.string_from_xpath("/entry/activity:object/id", entry),
diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex
index 502410c83..331cbc0b7 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -56,7 +56,7 @@ def remote_follow_path do
def handle_incoming(xml_string, options \\ []) do
with doc when doc != :error <- parse_document(xml_string) do
- with {:ok, actor_user} <- find_make_or_update_user(doc),
+ with {:ok, actor_user} <- find_make_or_update_actor(doc),
do: Pleroma.Instances.set_reachable(actor_user.ap_id)
entries = :xmerl_xpath.string('//entry', doc)
@@ -120,7 +120,7 @@ def handle_incoming(xml_string, options \\ []) do
end
def make_share(entry, doc, retweeted_activity) do
- with {:ok, actor} <- find_make_or_update_user(doc),
+ with {:ok, actor} <- find_make_or_update_actor(doc),
%Object{} = object <- Object.normalize(retweeted_activity),
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
{:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do
@@ -138,7 +138,7 @@ def handle_share(entry, doc) do
end
def make_favorite(entry, doc, favorited_activity) do
- with {:ok, actor} <- find_make_or_update_user(doc),
+ with {:ok, actor} <- find_make_or_update_actor(doc),
%Object{} = object <- Object.normalize(favorited_activity),
id when not is_nil(id) <- string_from_xpath("/entry/id", entry),
{:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do
@@ -264,11 +264,18 @@ def maybe_update_ostatus(doc, user) do
end
end
- def find_make_or_update_user(doc) do
+ def find_make_or_update_actor(doc) do
uri = string_from_xpath("//author/uri[1]", doc)
- with {:ok, user} <- find_or_make_user(uri) do
+ with {:ok, %User{} = user} <- find_or_make_user(uri),
+ {:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do
maybe_update(doc, user)
+ else
+ {:ap_enabled, true} ->
+ {:error, :invalid_protocol}
+
+ _ ->
+ {:error, :unknown_user}
end
end
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
index 4e8f3a0fc..d244dbcf7 100644
--- a/test/web/ostatus/ostatus_test.exs
+++ b/test/web/ostatus/ostatus_test.exs
@@ -426,7 +426,7 @@ test "find_or_make_user sets all the nessary input fields" do
}
end
- test "find_make_or_update_user takes an author element and returns an updated user" do
+ test "find_make_or_update_actor takes an author element and returns an updated user" do
uri = "https://social.heldscal.la/user/23211"
{:ok, user} = OStatus.find_or_make_user(uri)
@@ -439,14 +439,56 @@ test "find_make_or_update_user takes an author element and returns an updated us
doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
[author] = :xmerl_xpath.string('//author[1]', doc)
- {:ok, user} = OStatus.find_make_or_update_user(author)
+ {:ok, user} = OStatus.find_make_or_update_actor(author)
assert user.avatar["type"] == "Image"
assert user.name == old_name
assert user.bio == old_bio
- {:ok, user_again} = OStatus.find_make_or_update_user(author)
+ {:ok, user_again} = OStatus.find_make_or_update_actor(author)
assert user_again == user
end
+
+ test "find_or_make_user disallows protocol downgrade" do
+ user = insert(:user, %{local: true})
+ {:ok, user} = OStatus.find_or_make_user(user.ap_id)
+
+ assert User.ap_enabled?(user)
+
+ user =
+ insert(:user, %{
+ ap_id: "https://social.heldscal.la/user/23211",
+ info: %{ap_enabled: true},
+ local: false
+ })
+
+ assert User.ap_enabled?(user)
+
+ {:ok, user} = OStatus.find_or_make_user(user.ap_id)
+ assert User.ap_enabled?(user)
+ end
+
+ test "find_make_or_update_actor disallows protocol downgrade" do
+ user = insert(:user, %{local: true})
+ {:ok, user} = OStatus.find_or_make_user(user.ap_id)
+
+ assert User.ap_enabled?(user)
+
+ user =
+ insert(:user, %{
+ ap_id: "https://social.heldscal.la/user/23211",
+ info: %{ap_enabled: true},
+ local: false
+ })
+
+ assert User.ap_enabled?(user)
+
+ {:ok, user} = OStatus.find_or_make_user(user.ap_id)
+ assert User.ap_enabled?(user)
+
+ doc = XML.parse_document(File.read!("test/fixtures/23211.atom"))
+ [author] = :xmerl_xpath.string('//author[1]', doc)
+ {:error, :invalid_protocol} = OStatus.find_make_or_update_actor(author)
+ end
end
describe "gathering user info from a user id" do
From 6eb33e73035789fd9160e697617feb30a3070589 Mon Sep 17 00:00:00 2001
From: Maksim
Date: Wed, 31 Jul 2019 18:35:15 +0000
Subject: [PATCH 064/202] test for
Pleroma.Web.CommonAPI.Utils.get_by_id_or_ap_id
---
lib/pleroma/flake_id.ex | 10 ++++++++++
lib/pleroma/web/common_api/utils.ex | 7 ++++++-
test/flake_id_test.exs | 5 +++++
test/web/common_api/common_api_utils_test.exs | 20 +++++++++++++++++++
4 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/flake_id.ex b/lib/pleroma/flake_id.ex
index 58ab3650d..ca0610abc 100644
--- a/lib/pleroma/flake_id.ex
+++ b/lib/pleroma/flake_id.ex
@@ -66,6 +66,16 @@ def from_integer(integer) do
@spec get :: binary
def get, do: to_string(:gen_server.call(:flake, :get))
+ # checks that ID is is valid FlakeID
+ #
+ @spec is_flake_id?(String.t()) :: boolean
+ def is_flake_id?(id), do: is_flake_id?(String.to_charlist(id), true)
+ defp is_flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: is_flake_id?(cs, true)
+ defp is_flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: is_flake_id?(cs, true)
+ defp is_flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: is_flake_id?(cs, true)
+ defp is_flake_id?([], true), do: true
+ defp is_flake_id?(_, _), do: false
+
# -- Ecto.Type API
@impl Ecto.Type
def type, do: :uuid
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index d80fffa26..c8a743e8e 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -24,7 +24,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
# This is a hack for twidere.
def get_by_id_or_ap_id(id) do
activity =
- Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id)
+ with true <- Pleroma.FlakeId.is_flake_id?(id),
+ %Activity{} = activity <- Activity.get_by_id_with_object(id) do
+ activity
+ else
+ _ -> Activity.get_create_by_object_ap_id_with_object(id)
+ end
activity &&
if activity.data["type"] == "Create" do
diff --git a/test/flake_id_test.exs b/test/flake_id_test.exs
index ca2338041..85ed5bbdf 100644
--- a/test/flake_id_test.exs
+++ b/test/flake_id_test.exs
@@ -39,4 +39,9 @@ test "ecto type behaviour" do
assert dump(flake_s) == {:ok, flake}
assert dump(flake) == {:ok, flake}
end
+
+ test "is_flake_id?/1" do
+ assert is_flake_id?("9eoozpwTul5mjSEDRI")
+ refute is_flake_id?("http://example.com/activities/3ebbadd1-eb14-4e20-8118-b6f79c0c7b0b")
+ end
end
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index af320f31f..4b5666c29 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -360,4 +360,24 @@ test "for direct posts, a reply" do
assert third_user.ap_id in to
end
end
+
+ describe "get_by_id_or_ap_id/1" do
+ test "get activity by id" do
+ activity = insert(:note_activity)
+ %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id)
+ assert note.id == activity.id
+ end
+
+ test "get activity by ap_id" do
+ activity = insert(:note_activity)
+ %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"])
+ assert note.id == activity.id
+ end
+
+ test "get activity by object when type isn't `Create` " do
+ activity = insert(:like_activity)
+ %Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id)
+ assert like.data["object"] == activity.data["object"]
+ end
+ end
end
From 813c686dd77e6d441c235b2f7a57ac7911e249af Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Wed, 31 Jul 2019 22:05:12 +0300
Subject: [PATCH 065/202] Disallow following locked accounts over OStatus
---
lib/pleroma/web/ostatus/handlers/follow_handler.ex | 4 ++++
test/web/ostatus/ostatus_test.exs | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
index 03e4cbbb0..24513972e 100644
--- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex
+++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex
@@ -14,9 +14,13 @@ def handle(entry, doc) do
followed_uri when not is_nil(followed_uri) <-
XML.string_from_xpath("/entry/activity:object/id", entry),
{:ok, followed} <- OStatus.find_or_make_user(followed_uri),
+ {:locked, false} <- {:locked, followed.info.locked},
{:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
User.follow(actor, followed)
{:ok, activity}
+ else
+ {:locked, true} ->
+ {:error, "It's not possible to follow locked accounts over OStatus"}
end
end
end
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
index d244dbcf7..f8d389020 100644
--- a/test/web/ostatus/ostatus_test.exs
+++ b/test/web/ostatus/ostatus_test.exs
@@ -326,6 +326,14 @@ test "handle incoming follows" do
assert User.following?(follower, followed)
end
+ test "refuse following over OStatus if the followed's account is locked" do
+ incoming = File.read!("test/fixtures/follow.xml")
+ _user = insert(:user, info: %{locked: true}, ap_id: "https://pawoo.net/users/pekorino")
+
+ {:ok, [{:error, "It's not possible to follow locked accounts over OStatus"}]} =
+ OStatus.handle_incoming(incoming)
+ end
+
test "handle incoming unfollows with existing follow" do
incoming_follow = File.read!("test/fixtures/follow.xml")
{:ok, [_activity]} = OStatus.handle_incoming(incoming_follow)
From def0c49ead94d21a63bdc7323521b6d73ad4c0b2 Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Wed, 31 Jul 2019 23:03:06 +0300
Subject: [PATCH 066/202] Add a changelog entry for disallowing locked accounts
follows over OStatus
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b02ed243b..bd64b2259 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Security
- OStatus: eliminate the possibility of a protocol downgrade attack.
+- OStatus: prevent following locked accounts, bypassing the approval process.
### Changed
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
From 9ca45063556f3b75860d516577776a00536e90a8 Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Thu, 1 Aug 2019 15:53:37 +0700
Subject: [PATCH 067/202] Add configurable length limits for `User.bio` and
`User.name`
---
config/config.exs | 2 ++
docs/config.md | 2 ++
lib/pleroma/user.ex | 38 +++++++++++++++++++++-----------------
test/user_test.exs | 5 ++++-
4 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/config/config.exs b/config/config.exs
index 17770640a..aa4cdf409 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -253,6 +253,8 @@
skip_thread_containment: true,
limit_to_local_content: :unauthenticated,
dynamic_configuration: false,
+ user_bio_length: 5000,
+ user_name_length: 100,
external_user_synchronization: true
config :pleroma, :markup,
diff --git a/docs/config.md b/docs/config.md
index 02f86dc16..8f58eaf06 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -125,6 +125,8 @@ config :pleroma, Pleroma.Emails.Mailer,
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
* `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``.
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
+* `user_bio_length`: A user bio maximum length (default: `5000`)
+* `user_name_length`: A user name maximum length (default: `100`)
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api.
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 1adb82f32..776dbbe6d 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -149,10 +149,10 @@ def following_count(%User{} = user) do
end
def remote_user_creation(params) do
- params =
- params
- |> Map.put(:info, params[:info] || %{})
+ bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+ params = Map.put(params, :info, params[:info] || %{})
info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info])
changes =
@@ -161,8 +161,8 @@ def remote_user_creation(params) do
|> validate_required([:name, :ap_id])
|> unique_constraint(:nickname)
|> validate_format(:nickname, @email_regex)
- |> validate_length(:bio, max: 5000)
- |> validate_length(:name, max: 100)
+ |> validate_length(:bio, max: bio_limit)
+ |> validate_length(:name, max: name_limit)
|> put_change(:local, false)
|> put_embed(:info, info_cng)
@@ -185,22 +185,23 @@ def remote_user_creation(params) do
end
def update_changeset(struct, params \\ %{}) do
+ bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+
struct
|> cast(params, [:bio, :name, :avatar, :following])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
- |> validate_length(:bio, max: 5000)
- |> validate_length(:name, min: 1, max: 100)
+ |> validate_length(:bio, max: bio_limit)
+ |> validate_length(:name, min: 1, max: name_limit)
end
def upgrade_changeset(struct, params \\ %{}) do
- params =
- params
- |> Map.put(:last_refreshed_at, NaiveDateTime.utc_now())
+ bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
- info_cng =
- struct.info
- |> User.Info.user_upgrade(params[:info])
+ params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
+ info_cng = User.Info.user_upgrade(struct.info, params[:info])
struct
|> cast(params, [
@@ -213,8 +214,8 @@ def upgrade_changeset(struct, params \\ %{}) do
])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
- |> validate_length(:bio, max: 5000)
- |> validate_length(:name, max: 100)
+ |> validate_length(:bio, max: bio_limit)
+ |> validate_length(:name, max: name_limit)
|> put_embed(:info, info_cng)
end
@@ -241,6 +242,9 @@ def reset_password(%User{id: user_id} = user, data) do
end
def register_changeset(struct, params \\ %{}, opts \\ []) do
+ bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+
need_confirmation? =
if is_nil(opts[:need_confirmation]) do
Pleroma.Config.get([:instance, :account_activation_required])
@@ -261,8 +265,8 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
- |> validate_length(:bio, max: 1000)
- |> validate_length(:name, min: 1, max: 100)
+ |> validate_length(:bio, max: bio_limit)
+ |> validate_length(:name, min: 1, max: name_limit)
|> put_change(:info, info_change)
changeset =
diff --git a/test/user_test.exs b/test/user_test.exs
index 556df45fd..dfa91a106 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -525,7 +525,10 @@ test "it has required fields" do
end
test "it restricts some sizes" do
- [bio: 5000, name: 100]
+ bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
+ name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
+
+ [bio: bio_limit, name: name_limit]
|> Enum.each(fn {field, size} ->
string = String.pad_leading(".", size)
cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
From f98235f2adfff290d95c7353c63225c07e5f86ff Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn
Date: Thu, 1 Aug 2019 16:33:36 +0700
Subject: [PATCH 068/202] Clean up tests output
---
test/web/admin_api/admin_api_controller_test.exs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 824ad23e6..f61499a22 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1922,7 +1922,10 @@ test "queues key as atom", %{conn: conn} do
temp_file = "config/test.exported_from_db.secret.exs"
+ Mix.shell(Mix.Shell.Quiet)
+
on_exit(fn ->
+ Mix.shell(Mix.Shell.IO)
:ok = File.rm(temp_file)
end)
From 81412240e6e6ca60a7fcece5eff056722d868d2e Mon Sep 17 00:00:00 2001
From: rinpatch
Date: Thu, 1 Aug 2019 14:15:18 +0300
Subject: [PATCH 069/202] Fix Invalid SemVer version generation
when the current branch does not have commits ahead of tag/checked out on a tag
---
CHANGELOG.md | 1 +
mix.exs | 23 ++++++++++++++++++-----
2 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd64b2259..6fdc432ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
- Not being able to access the Mastodon FE login page on private instances
+- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
diff --git a/mix.exs b/mix.exs
index 2a8fe2e9d..dfff53d57 100644
--- a/mix.exs
+++ b/mix.exs
@@ -190,12 +190,13 @@ defp version(version) do
tag = String.trim(tag),
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
describe = String.trim(describe),
- ahead <- String.replace(describe, tag, "") do
+ ahead <- String.replace(describe, tag, ""),
+ ahead <- String.trim_leading(ahead, "-") do
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
else
_ ->
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
- {nil, "-0-g" <> String.trim(commit_hash)}
+ {nil, "0-g" <> String.trim(commit_hash)}
end
if git_tag && version != git_tag do
@@ -207,14 +208,15 @@ defp version(version) do
# Branch name as pre-release version component, denoted with a dot
branch_name =
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
+ branch_name <- String.trim(branch_name),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
- true <- branch_name != "master" do
+ true <- branch_name not in ["master", "HEAD"] do
branch_name =
branch_name
|> String.trim()
|> String.replace(identifier_filter, "-")
- "." <> branch_name
+ branch_name
end
build_name =
@@ -234,6 +236,17 @@ defp version(version) do
env_override -> env_override
end
+ # Pre-release version, denoted by appending a hyphen
+ # and a series of dot separated identifiers
+ pre_release =
+ [git_pre_release, branch_name]
+ |> Enum.filter(fn string -> string && string != "" end)
+ |> Enum.join(".")
+ |> (fn
+ "" -> nil
+ string -> "-" <> String.replace(string, identifier_filter, "-")
+ end).()
+
# Build metadata, denoted with a plus sign
build_metadata =
[build_name, env_name]
@@ -244,7 +257,7 @@ defp version(version) do
string -> "+" <> String.replace(string, identifier_filter, "-")
end).()
- [version, git_pre_release, branch_name, build_metadata]
+ [version, pre_release, build_metadata]
|> Enum.filter(fn string -> string && string != "" end)
|> Enum.join()
end
From fd4b7239cd6f44a25c9aa4195750e94e0612a3b1 Mon Sep 17 00:00:00 2001
From: lain
Date: Thu, 1 Aug 2019 17:25:46 +0200
Subject: [PATCH 070/202] nothing
From f88560accd801ac88c60344cef93ef00cf136069 Mon Sep 17 00:00:00 2001
From: lain
Date: Fri, 2 Aug 2019 11:55:41 +0200
Subject: [PATCH 071/202] Conversations: Add recipient list to conversation
participation.
This enables to address the same group of people every time.
---
lib/pleroma/conversation.ex | 11 ++++++
lib/pleroma/conversation/participation.ex | 4 +++
.../participation_recipient_ship.ex | 34 +++++++++++++++++++
lib/pleroma/user.ex | 7 ++++
.../20190205114625_create_thread_mutes.exs | 2 +-
...ersation_participation_recipient_ships.exs | 13 +++++++
test/conversation/participation_test.exs | 30 ++++++++++++++++
7 files changed, 100 insertions(+), 1 deletion(-)
create mode 100644 lib/pleroma/conversation/participation_recipient_ship.ex
create mode 100644 priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index bc97b39ca..fb0dfedca 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Conversation do
alias Pleroma.Conversation.Participation
+ alias Pleroma.Conversation.Participation.RecipientShip
alias Pleroma.Repo
alias Pleroma.User
use Ecto.Schema
@@ -39,6 +40,15 @@ def get_for_ap_id(ap_id) do
Repo.get_by(__MODULE__, ap_id: ap_id)
end
+ def maybe_set_recipients(participation, activity) do
+ participation = Repo.preload(participation, :recipients)
+
+ if participation.recipients |> Enum.empty?() do
+ recipients = User.get_all_by_ap_id(activity.recipients)
+ RecipientShip.create(recipients, participation)
+ end
+ end
+
@doc """
This will
1. Create a conversation if there isn't one already
@@ -60,6 +70,7 @@ def create_or_bump_for(activity, opts \\ []) do
{:ok, participation} =
Participation.create_for_user_and_conversation(user, conversation, opts)
+ maybe_set_recipients(participation, activity)
participation
end)
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 77b3f61e9..121efb671 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Conversation.Participation do
use Ecto.Schema
alias Pleroma.Conversation
+ alias Pleroma.Conversation.Participation.RecipientShip
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -17,6 +18,9 @@ defmodule Pleroma.Conversation.Participation do
field(:read, :boolean, default: false)
field(:last_activity_id, Pleroma.FlakeId, virtual: true)
+ has_many(:recipient_ships, RecipientShip)
+ has_many(:recipients, through: [:recipient_ships, :user])
+
timestamps()
end
diff --git a/lib/pleroma/conversation/participation_recipient_ship.ex b/lib/pleroma/conversation/participation_recipient_ship.ex
new file mode 100644
index 000000000..27c0c89cd
--- /dev/null
+++ b/lib/pleroma/conversation/participation_recipient_ship.ex
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Conversation.Participation.RecipientShip do
+ use Ecto.Schema
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.User
+ alias Pleroma.Repo
+
+ import Ecto.Changeset
+
+ schema "conversation_participation_recipient_ships" do
+ belongs_to(:user, User, type: Pleroma.FlakeId)
+ belongs_to(:participation, Participation)
+ end
+
+ def creation_cng(struct, params) do
+ struct
+ |> cast(params, [:user_id, :participation_id])
+ |> validate_required([:user_id, :participation_id])
+ end
+
+ def create(%User{} = user, participation), do: create([user], participation)
+
+ def create(users, participation) do
+ Enum.each(users, fn user ->
+ %__MODULE__{}
+ |> creation_cng(%{user_id: user.id, participation_id: participation.id})
+ |> Repo.insert!()
+ end)
+ end
+end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 6e2fd3af8..a021e77f0 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -450,6 +450,13 @@ def get_by_ap_id(ap_id) do
Repo.get_by(User, ap_id: ap_id)
end
+ def get_all_by_ap_id(ap_ids) do
+ from(u in __MODULE__,
+ where: u.ap_id in ^ap_ids
+ )
+ |> Repo.all()
+ end
+
# This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
# of the ap_id and the domain and tries to get that user
def get_by_guessed_nickname(ap_id) do
diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs
index 7e44db121..baaf07253 100644
--- a/priv/repo/migrations/20190205114625_create_thread_mutes.exs
+++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs
@@ -6,7 +6,7 @@ def change do
add :user_id, references(:users, type: :uuid, on_delete: :delete_all)
add :context, :string
end
-
+
create_if_not_exists unique_index(:thread_mutes, [:user_id, :context], name: :unique_index)
end
end
diff --git a/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs b/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs
new file mode 100644
index 000000000..c6e3469d5
--- /dev/null
+++ b/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs
@@ -0,0 +1,13 @@
+defmodule Pleroma.Repo.Migrations.CreateConversationParticipationRecipientShips do
+ use Ecto.Migration
+
+ def change do
+ create_if_not_exists table(:conversation_participation_recipient_ships) do
+ add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
+ add(:participation_id, references(:conversation_participations, on_delete: :delete_all))
+ end
+
+ create_if_not_exists index(:conversation_participation_recipient_ships, [:user_id])
+ create_if_not_exists index(:conversation_participation_recipient_ships, [:participation_id])
+ end
+end
diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs
index 2a03e5d67..4a3c397bd 100644
--- a/test/conversation/participation_test.exs
+++ b/test/conversation/participation_test.exs
@@ -8,6 +8,36 @@ defmodule Pleroma.Conversation.ParticipationTest do
alias Pleroma.Conversation.Participation
alias Pleroma.Web.CommonAPI
+ test "for a new conversation, it sets the recipents of the participation" do
+ user = insert(:user)
+ other_user = insert(:user)
+ third_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
+
+ [participation] = Participation.for_user(user)
+ participation = Pleroma.Repo.preload(participation, :recipients)
+
+ assert length(participation.recipients) == 2
+ assert user in participation.recipients
+ assert other_user in participation.recipients
+
+ # Mentioning another user in the same conversation will not add a new recipients.
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{
+ "in_reply_to_status_id" => activity.id,
+ "status" => "Hey @#{third_user.nickname}.",
+ "visibility" => "direct"
+ })
+
+ [participation] = Participation.for_user(user)
+ participation = Pleroma.Repo.preload(participation, :recipients)
+
+ assert length(participation.recipients) == 2
+ end
+
test "it creates a participation for a conversation and a user" do
user = insert(:user)
conversation = insert(:conversation)
From 56b1c3af13c9519e13da688bdbbfdd8d69cda4ac Mon Sep 17 00:00:00 2001
From: lain
Date: Fri, 2 Aug 2019 15:05:27 +0200
Subject: [PATCH 072/202] CommonAPI: Extend api with conversation replies.
---
lib/pleroma/conversation/participation.ex | 6 +++++
lib/pleroma/web/common_api/common_api.ex | 20 ++++++++++-----
lib/pleroma/web/common_api/utils.ex | 27 ++++++++++++++------
test/web/common_api/common_api_test.exs | 30 +++++++++++++++++++++++
4 files changed, 70 insertions(+), 13 deletions(-)
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 121efb671..f1e1a6958 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -93,4 +93,10 @@ def for_user_with_last_activity_id(user, params \\ %{}) do
end)
|> Enum.filter(& &1.last_activity_id)
end
+
+ def get(nil), do: nil
+
+ def get(id) do
+ Repo.get(__MODULE__, id)
+ end
end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 2db58324b..86e95cd0f 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Activity
+ alias Pleroma.Conversation.Participation
alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.ThreadMute
@@ -171,21 +172,25 @@ defp normalize_and_validate_choice_indices(choices, count) do
end)
end
- def get_visibility(%{"visibility" => visibility}, in_reply_to)
+ def get_visibility(_, _, %Participation{}) do
+ {"direct", "direct"}
+ end
+
+ def get_visibility(%{"visibility" => visibility}, in_reply_to, _)
when visibility in ~w{public unlisted private direct},
do: {visibility, get_replied_to_visibility(in_reply_to)}
- def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to) do
+ def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to, _) do
visibility = {:list, String.to_integer(list_id)}
{visibility, get_replied_to_visibility(in_reply_to)}
end
- def get_visibility(_, in_reply_to) when not is_nil(in_reply_to) do
+ def get_visibility(_, in_reply_to, _) when not is_nil(in_reply_to) do
visibility = get_replied_to_visibility(in_reply_to)
{visibility, visibility}
end
- def get_visibility(_, in_reply_to), do: {"public", get_replied_to_visibility(in_reply_to)}
+ def get_visibility(_, in_reply_to, _), do: {"public", get_replied_to_visibility(in_reply_to)}
def get_replied_to_visibility(nil), do: nil
@@ -201,7 +206,9 @@ def post(user, %{"status" => status} = data) do
with status <- String.trim(status),
attachments <- attachments_from_ids(data),
in_reply_to <- get_replied_to_activity(data["in_reply_to_status_id"]),
- {visibility, in_reply_to_visibility} <- get_visibility(data, in_reply_to),
+ in_reply_to_conversation <- Participation.get(data["in_reply_to_conversation_id"]),
+ {visibility, in_reply_to_visibility} <-
+ get_visibility(data, in_reply_to, in_reply_to_conversation),
{_, false} <-
{:private_to_public, in_reply_to_visibility == "direct" && visibility != "direct"},
{content_html, mentions, tags} <-
@@ -214,7 +221,8 @@ def post(user, %{"status" => status} = data) do
mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.ap_id),
addressed_users <- get_addressed_users(mentioned_users, data["to"]),
{poll, poll_emoji} <- make_poll_data(data),
- {to, cc} <- get_to_and_cc(user, addressed_users, in_reply_to, visibility),
+ {to, cc} <-
+ get_to_and_cc(user, addressed_users, in_reply_to, visibility, in_reply_to_conversation),
context <- make_context(in_reply_to),
cw <- data["spoiler_text"] || "",
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index d80fffa26..e70ba7d43 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Calendar.Strftime
alias Pleroma.Activity
alias Pleroma.Config
+ alias Pleroma.Conversation.Participation
alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.Plugs.AuthenticationPlug
@@ -64,9 +65,21 @@ def attachments_from_ids_descs(ids, descs_str) do
end)
end
- @spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
+ @spec get_to_and_cc(
+ User.t(),
+ list(String.t()),
+ Activity.t() | nil,
+ String.t(),
+ Participation.t() | nil
+ ) ::
{list(String.t()), list(String.t())}
- def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
+
+ def get_to_and_cc(_, _, _, _, %Participation{} = participation) do
+ participation = Repo.preload(participation, :recipients)
+ {Enum.map(participation.recipients, & &1.ap_id), []}
+ end
+
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "public", _) do
to = [Pleroma.Constants.as_public() | mentioned_users]
cc = [user.follower_address]
@@ -77,7 +90,7 @@ def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
end
end
- def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted", _) do
to = [user.follower_address | mentioned_users]
cc = [Pleroma.Constants.as_public()]
@@ -88,12 +101,12 @@ def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do
end
end
- def get_to_and_cc(user, mentioned_users, inReplyTo, "private") do
- {to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct")
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "private", _) do
+ {to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct", nil)
{[user.follower_address | to], cc}
end
- def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct") do
+ def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do
if inReplyTo do
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
else
@@ -101,7 +114,7 @@ def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct") do
end
end
- def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}), do: {mentions, []}
+ def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}, _), do: {mentions, []}
def get_addressed_users(_, to) when is_list(to) do
User.get_ap_ids_by_nicknames(to)
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 16b3f121d..e2a5bf117 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.CommonAPITest do
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Conversation.Participation
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -12,6 +13,35 @@ defmodule Pleroma.Web.CommonAPITest do
import Pleroma.Factory
+ test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
+ har = insert(:user)
+ jafnhar = insert(:user)
+ tridi = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(har, %{
+ "status" => "@#{jafnhar.nickname} hey",
+ "visibility" => "direct"
+ })
+
+ assert har.ap_id in activity.recipients
+ assert jafnhar.ap_id in activity.recipients
+
+ [participation] = Participation.for_user(har)
+
+ {:ok, activity} =
+ CommonAPI.post(har, %{
+ "status" => "I don't really like @#{tridi.nickname}",
+ "visibility" => "direct",
+ "in_reply_to_status_id" => activity.id,
+ "in_reply_to_conversation_id" => participation.id
+ })
+
+ assert har.ap_id in activity.recipients
+ assert jafnhar.ap_id in activity.recipients
+ refute tridi.ap_id in activity.recipients
+ end
+
test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
har = insert(:user)
jafnhar = insert(:user)
From d93d7779151c811e991e99098e64c1da2c783d68 Mon Sep 17 00:00:00 2001
From: feld
Date: Fri, 2 Aug 2019 17:07:09 +0000
Subject: [PATCH 073/202] Fix/mediaproxy whitelist base url
---
CHANGELOG.md | 1 +
lib/pleroma/web/media_proxy/media_proxy.ex | 14 ++++-
.../mastodon_api_controller_test.exs | 34 -----------
test/web/media_proxy/media_proxy_test.exs | 58 ++++++++++++-------
4 files changed, 51 insertions(+), 56 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6fdc432ef..4fa9ffd9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub S2S: remote user deletions now work the same as local user deletions.
- Not being able to access the Mastodon FE login page on private instances
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
+- Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.
### Added
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index a661e9bb7..1725ab071 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.MediaProxy do
alias Pleroma.Config
+ alias Pleroma.Upload
alias Pleroma.Web
@base64_opts [padding: false]
@@ -26,7 +27,18 @@ defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
defp whitelisted?(url) do
%{host: domain} = URI.parse(url)
- Enum.any?(Config.get([:media_proxy, :whitelist]), fn pattern ->
+ mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
+
+ upload_base_url_domain =
+ if !is_nil(Config.get([Upload, :base_url])) do
+ [URI.parse(Config.get([Upload, :base_url])).host]
+ else
+ []
+ end
+
+ whitelist = mediaproxy_whitelist ++ upload_base_url_domain
+
+ Enum.any?(whitelist, fn pattern ->
String.equivalent?(domain, pattern)
end)
end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 66016c886..e49c4cc22 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -1671,40 +1671,6 @@ test "returns uploaded image", %{conn: conn, image: image} do
object = Repo.get(Object, media["id"])
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
end
-
- test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
- Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
-
- proxy_url = "https://cache.pleroma.social"
- Pleroma.Config.put([:media_proxy, :enabled], true)
- Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
-
- media =
- conn
- |> post("/api/v1/media", %{"file" => image})
- |> json_response(:ok)
-
- assert String.starts_with?(media["url"], proxy_url)
- end
-
- test "returns media url when proxy is enabled but media url is whitelisted", %{
- conn: conn,
- image: image
- } do
- media_url = "https://media.pleroma.social"
- Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
-
- Pleroma.Config.put([:media_proxy, :enabled], true)
- Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
- Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
-
- media =
- conn
- |> post("/api/v1/media", %{"file" => image})
- |> json_response(:ok)
-
- assert String.starts_with?(media["url"], media_url)
- end
end
describe "locked accounts" do
diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs
index edbbf9b66..0c94755df 100644
--- a/test/web/media_proxy/media_proxy_test.exs
+++ b/test/web/media_proxy/media_proxy_test.exs
@@ -171,21 +171,6 @@ test "preserve unicode characters" do
encoded = url(url)
assert decode_result(encoded) == url
end
-
- test "does not change whitelisted urls" do
- upload_config = Pleroma.Config.get([Pleroma.Upload])
- media_url = "https://media.pleroma.social"
- Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
- Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
- Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
-
- url = "#{media_url}/static/logo.png"
- encoded = url(url)
-
- assert String.starts_with?(encoded, media_url)
-
- Pleroma.Config.put([Pleroma.Upload], upload_config)
- end
end
describe "when disabled" do
@@ -215,12 +200,43 @@ defp decode_result(encoded) do
decoded
end
- test "mediaproxy whitelist" do
- Pleroma.Config.put([:media_proxy, :enabled], true)
- Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
- url = "https://feld.me/foo.png"
+ describe "whitelist" do
+ setup do
+ Pleroma.Config.put([:media_proxy, :enabled], true)
+ :ok
+ end
- unencoded = url(url)
- assert unencoded == url
+ test "mediaproxy whitelist" do
+ Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
+ url = "https://feld.me/foo.png"
+
+ unencoded = url(url)
+ assert unencoded == url
+ end
+
+ test "does not change whitelisted urls" do
+ Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"])
+ Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
+
+ media_url = "https://mycdn.akamai.com"
+
+ url = "#{media_url}/static/logo.png"
+ encoded = url(url)
+
+ assert String.starts_with?(encoded, media_url)
+ end
+
+ test "ensure Pleroma.Upload base_url is always whitelisted" do
+ upload_config = Pleroma.Config.get([Pleroma.Upload])
+ media_url = "https://media.pleroma.social"
+ Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
+
+ url = "#{media_url}/static/logo.png"
+ encoded = url(url)
+
+ assert String.starts_with?(encoded, media_url)
+
+ Pleroma.Config.put([Pleroma.Upload], upload_config)
+ end
end
end
From eee98aaa738c1aa5f2e4203a61b67648d62965c8 Mon Sep 17 00:00:00 2001
From: lain
Date: Fri, 2 Aug 2019 19:53:08 +0200
Subject: [PATCH 074/202] Pleroma API: Add endpoint to get conversation
statuses.
---
lib/pleroma/web/controller_helper.ex | 76 +++++++++++++++++++
.../mastodon_api/mastodon_api_controller.ex | 68 +----------------
.../web/pleroma_api/pleroma_api_controller.ex | 49 ++++++++++++
lib/pleroma/web/router.ex | 9 +++
test/web/common_api/common_api_utils_test.exs | 16 ++--
.../pleroma_api_controller_test.exs | 45 +++++++++++
6 files changed, 189 insertions(+), 74 deletions(-)
create mode 100644 lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
create mode 100644 test/web/pleroma_api/pleroma_api_controller_test.exs
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex
index 8a753bb4f..eeac9f503 100644
--- a/lib/pleroma/web/controller_helper.ex
+++ b/lib/pleroma/web/controller_helper.ex
@@ -33,4 +33,80 @@ defp param_to_integer(val, default) when is_binary(val) do
end
defp param_to_integer(_, default), do: default
+
+ def add_link_headers(
+ conn,
+ method,
+ activities,
+ param \\ nil,
+ params \\ %{},
+ func3 \\ nil,
+ func4 \\ nil
+ ) do
+ params =
+ conn.params
+ |> Map.drop(["since_id", "max_id", "min_id"])
+ |> Map.merge(params)
+
+ last = List.last(activities)
+
+ func3 = func3 || (&mastodon_api_url/3)
+ func4 = func4 || (&mastodon_api_url/4)
+
+ if last do
+ max_id = last.id
+
+ limit =
+ params
+ |> Map.get("limit", "20")
+ |> String.to_integer()
+
+ min_id =
+ if length(activities) <= limit do
+ activities
+ |> List.first()
+ |> Map.get(:id)
+ else
+ activities
+ |> Enum.at(limit * -1)
+ |> Map.get(:id)
+ end
+
+ {next_url, prev_url} =
+ if param do
+ {
+ func4.(
+ Pleroma.Web.Endpoint,
+ method,
+ param,
+ Map.merge(params, %{max_id: max_id})
+ ),
+ func4.(
+ Pleroma.Web.Endpoint,
+ method,
+ param,
+ Map.merge(params, %{min_id: min_id})
+ )
+ }
+ else
+ {
+ func3.(
+ Pleroma.Web.Endpoint,
+ method,
+ Map.merge(params, %{max_id: max_id})
+ ),
+ func3.(
+ Pleroma.Web.Endpoint,
+ method,
+ Map.merge(params, %{min_id: min_id})
+ )
+ }
+ end
+
+ conn
+ |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
+ else
+ conn
+ end
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 174e93468..0deeab2be 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -5,7 +5,8 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
- import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+ import Pleroma.Web.ControllerHelper,
+ only: [json_response: 3, add_link_headers: 5, add_link_headers: 4, add_link_headers: 3]
alias Ecto.Changeset
alias Pleroma.Activity
@@ -342,71 +343,6 @@ def custom_emojis(conn, _params) do
json(conn, mastodon_emoji)
end
- defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
- params =
- conn.params
- |> Map.drop(["since_id", "max_id", "min_id"])
- |> Map.merge(params)
-
- last = List.last(activities)
-
- if last do
- max_id = last.id
-
- limit =
- params
- |> Map.get("limit", "20")
- |> String.to_integer()
-
- min_id =
- if length(activities) <= limit do
- activities
- |> List.first()
- |> Map.get(:id)
- else
- activities
- |> Enum.at(limit * -1)
- |> Map.get(:id)
- end
-
- {next_url, prev_url} =
- if param do
- {
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- param,
- Map.merge(params, %{max_id: max_id})
- ),
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- param,
- Map.merge(params, %{min_id: min_id})
- )
- }
- else
- {
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- Map.merge(params, %{max_id: max_id})
- ),
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- Map.merge(params, %{min_id: min_id})
- )
- }
- end
-
- conn
- |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
- else
- conn
- end
- end
-
def home_timeline(%{assigns: %{user: user}} = conn, params) do
params =
params
diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
new file mode 100644
index 000000000..b677892ed
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7]
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.MastodonAPI.StatusView
+ alias Pleroma.Repo
+
+ def conversation_statuses(
+ %{assigns: %{user: user}} = conn,
+ %{"id" => participation_id} = params
+ ) do
+ params =
+ params
+ |> Map.put("blocking_user", user)
+ |> Map.put("muting_user", user)
+ |> Map.put("user", user)
+
+ participation =
+ participation_id
+ |> Participation.get()
+ |> Repo.preload(:conversation)
+
+ if user.id == participation.user_id do
+ activities =
+ participation.conversation.ap_id
+ |> ActivityPub.fetch_activities_for_context(params)
+ |> Enum.reverse()
+
+ conn
+ |> add_link_headers(
+ :conversation_statuses,
+ activities,
+ participation_id,
+ params,
+ nil,
+ &pleroma_api_url/4
+ )
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
+ end
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 0689d69fb..40298538a 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -257,6 +257,15 @@ defmodule Pleroma.Web.Router do
end
end
+ scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
+ pipe_through(:authenticated_api)
+
+ scope [] do
+ pipe_through(:oauth_write)
+ get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
+ end
+ end
+
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through(:authenticated_api)
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs
index af320f31f..7510c8def 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -239,7 +239,7 @@ test "for public posts, not a reply" do
mentioned_user = insert(:user)
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "public")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "public", nil)
assert length(to) == 2
assert length(cc) == 1
@@ -256,7 +256,7 @@ test "for public posts, a reply" do
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"})
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public", nil)
assert length(to) == 3
assert length(cc) == 1
@@ -272,7 +272,7 @@ test "for unlisted posts, not a reply" do
mentioned_user = insert(:user)
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "unlisted")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "unlisted", nil)
assert length(to) == 2
assert length(cc) == 1
@@ -289,7 +289,7 @@ test "for unlisted posts, a reply" do
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"})
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted", nil)
assert length(to) == 3
assert length(cc) == 1
@@ -305,7 +305,7 @@ test "for private posts, not a reply" do
mentioned_user = insert(:user)
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil)
assert length(to) == 2
assert length(cc) == 0
@@ -321,7 +321,7 @@ test "for private posts, a reply" do
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"})
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil)
assert length(to) == 3
assert length(cc) == 0
@@ -336,7 +336,7 @@ test "for direct posts, not a reply" do
mentioned_user = insert(:user)
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil)
assert length(to) == 1
assert length(cc) == 0
@@ -351,7 +351,7 @@ test "for direct posts, a reply" do
{:ok, activity} = CommonAPI.post(third_user, %{"status" => "uguu"})
mentions = [mentioned_user.ap_id]
- {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct")
+ {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil)
assert length(to) == 2
assert length(cc) == 0
diff --git a/test/web/pleroma_api/pleroma_api_controller_test.exs b/test/web/pleroma_api/pleroma_api_controller_test.exs
new file mode 100644
index 000000000..43104e36e
--- /dev/null
+++ b/test/web/pleroma_api/pleroma_api_controller_test.exs
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ test "/api/v1/pleroma/conversations/:id/statuses", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+ third_user = insert(:user)
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{"status" => "Hi @#{third_user.nickname}!", "visibility" => "direct"})
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}!", "visibility" => "direct"})
+
+ [participation] = Participation.for_user(other_user)
+
+ {:ok, activity_two} =
+ CommonAPI.post(other_user, %{
+ "status" => "Hi!",
+ "in_reply_to_status_id" => activity.id,
+ "in_reply_to_conversation_id" => participation.id
+ })
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses")
+ |> json_response(200)
+
+ assert length(result) == 2
+
+ id_one = activity.id
+ id_two = activity_two.id
+ assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result
+ end
+end
From 8815f07058f4bdf61355758cbe740288e9551435 Mon Sep 17 00:00:00 2001
From: "Haelwenn (lanodan) Monnier"
Date: Fri, 2 Aug 2019 23:30:47 +0200
Subject: [PATCH 075/202] tasks/pleroma/user.ex: Fix documentation of --max-use
and --expire-at
Closes: https://git.pleroma.social/pleroma/pleroma/issues/1155
[ci skip]
---
lib/mix/tasks/pleroma/user.ex | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index c9b84b8f9..a3f8bc945 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -31,8 +31,8 @@ defmodule Mix.Tasks.Pleroma.User do
mix pleroma.user invite [OPTION...]
Options:
- - `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05")
- - `--max_use NUMBER` - maximum numbers of token uses
+ - `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
+ - `--max-use NUMBER` - maximum numbers of token uses
## List generated invites
From 7efca4317b568c408a10b71799f9b8261ac5e8e6 Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson
Date: Wed, 31 Jul 2019 19:35:14 -0400
Subject: [PATCH 076/202] Basic working Dockerfile
No fancy script or minit automatic migration, etc, but if you start
the docker image and go in and manually do everything, it works.
---
Dockerfile | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 Dockerfile
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..667c01b39
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,32 @@
+FROM rinpatch/elixir:1.9.0-rc.0-alpine as build
+
+COPY . .
+
+ENV MIX_ENV prod
+
+RUN apk add git gcc g++ musl-dev make &&\
+ echo "import Mix.Config" > config/prod.secret.exs &&\
+ mix local.hex --force &&\
+ mix local.rebar --force
+
+RUN mix deps.get --only prod &&\
+ mkdir release &&\
+ mix release --path release
+
+FROM alpine:latest
+
+RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
+ apk update &&\
+ apk add ncurses postgresql-client
+
+RUN adduser --system --shell /bin/false --home /opt/pleroma pleroma &&\
+ mkdir -p /var/lib/pleroma/uploads &&\
+ chown -R pleroma /var/lib/pleroma &&\
+ mkdir -p /var/lib/pleroma/static &&\
+ chown -R pleroma /var/lib/pleroma &&\
+ mkdir -p /etc/pleroma &&\
+ chown -R pleroma /etc/pleroma
+
+USER pleroma
+
+COPY --from=build --chown=pleroma:0 /release/ /opt/pleroma/
From 4a418698db71016447f2f246f7c5579b3dc0b08c Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson
Date: Fri, 2 Aug 2019 22:28:48 -0400
Subject: [PATCH 077/202] Create docker.exs and docker-entrypoint + round out
Dockerfile
At this point, the implementation is completely working and has been
tested running live and federating with other instances.
---
Dockerfile | 23 ++++++++++-----
config/docker.exs | 67 ++++++++++++++++++++++++++++++++++++++++++++
docker-entrypoint.sh | 14 +++++++++
3 files changed, 97 insertions(+), 7 deletions(-)
create mode 100644 config/docker.exs
create mode 100755 docker-entrypoint.sh
diff --git a/Dockerfile b/Dockerfile
index 667c01b39..2f438c952 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ FROM rinpatch/elixir:1.9.0-rc.0-alpine as build
COPY . .
-ENV MIX_ENV prod
+ENV MIX_ENV=prod
RUN apk add git gcc g++ musl-dev make &&\
echo "import Mix.Config" > config/prod.secret.exs &&\
@@ -15,18 +15,27 @@ RUN mix deps.get --only prod &&\
FROM alpine:latest
+ARG HOME=/opt/pleroma
+ARG DATA=/var/lib/pleroma
+
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
apk update &&\
apk add ncurses postgresql-client
-RUN adduser --system --shell /bin/false --home /opt/pleroma pleroma &&\
- mkdir -p /var/lib/pleroma/uploads &&\
- chown -R pleroma /var/lib/pleroma &&\
- mkdir -p /var/lib/pleroma/static &&\
- chown -R pleroma /var/lib/pleroma &&\
+RUN adduser --system --shell /bin/false --home ${HOME} pleroma &&\
+ mkdir -p ${DATA}/uploads &&\
+ mkdir -p ${DATA}/static &&\
+ chown -R pleroma ${DATA} &&\
mkdir -p /etc/pleroma &&\
chown -R pleroma /etc/pleroma
USER pleroma
-COPY --from=build --chown=pleroma:0 /release/ /opt/pleroma/
+COPY --from=build --chown=pleroma:0 /release ${HOME}
+
+COPY ./config/docker.exs /etc/pleroma/config.exs
+COPY ./docker-entrypoint.sh ${HOME}
+
+EXPOSE 4000
+
+ENTRYPOINT ["/opt/pleroma/docker-entrypoint.sh"]
diff --git a/config/docker.exs b/config/docker.exs
new file mode 100644
index 000000000..c07f0b753
--- /dev/null
+++ b/config/docker.exs
@@ -0,0 +1,67 @@
+import Config
+
+config :pleroma, Pleroma.Web.Endpoint,
+ url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
+ http: [ip: {0, 0, 0, 0}, port: 4000]
+
+config :pleroma, :instance,
+ name: System.get_env("INSTANCE_NAME", "Pleroma"),
+ email: System.get_env("ADMIN_EMAIL"),
+ notify_email: System.get_env("NOTIFY_EMAIL"),
+ limit: 5000,
+ registrations_open: false,
+ dynamic_configuration: true
+
+config :pleroma, Pleroma.Repo,
+ adapter: Ecto.Adapters.Postgres,
+ username: System.get_env("DB_USER", "pleroma"),
+ password: System.fetch_env!("DB_PASS"),
+ database: System.get_env("DB_NAME", "pleroma"),
+ hostname: System.get_env("DB_HOST", "db"),
+ pool_size: 10
+
+# Configure web push notifications
+config :web_push_encryption, :vapid_details,
+ subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
+
+config :pleroma, :database, rum_enabled: false
+config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
+config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
+
+# We can't store the secrets in this file, since this is baked into the docker image
+if not File.exists?("/var/lib/pleroma/secret.exs") do
+ secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
+ signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
+ {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
+
+ secret_file = EEx.eval_string(
+ """
+ import Config
+
+ config :pleroma, Pleroma.Web.Endpoint,
+ secret_key_base: "<%= secret %>",
+ signing_salt: "<%= signing_salt %>"
+
+ config :web_push_encryption, :vapid_details,
+ public_key: "<%= web_push_public_key %>",
+ private_key: "<%= web_push_private_key %>"
+ """,
+ secret: secret,
+ signing_salt: signing_salt,
+ web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
+ web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
+ )
+
+ File.write("/var/lib/pleroma/secret.exs", secret_file)
+end
+
+import_config("/var/lib/pleroma/secret.exs")
+
+# For additional user config
+if File.exists?("/var/lib/pleroma/config.exs"),
+ do: import_config("/var/lib/pleroma/config.exs"),
+ else: File.write("/var/lib/pleroma/config.exs", """
+ import Config
+
+ # For additional configuration outside of environmental variables
+ """)
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 000000000..f56f8c50a
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,14 @@
+#!/bin/ash
+
+set -e
+
+echo "-- Waiting for database..."
+while ! pg_isready -U ${DB_USER:-pleroma} -d postgres://${DB_HOST:-db}:5432/${DB_NAME:-pleroma} -t 1; do
+ sleep 1s
+done
+
+echo "-- Running migrations..."
+$HOME/bin/pleroma_ctl migrate
+
+echo "-- Starting!"
+exec $HOME/bin/pleroma start
From c86db959cb3a3f4a4f79833747d5fa8ecff0d0c7 Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson
Date: Fri, 2 Aug 2019 22:40:31 -0400
Subject: [PATCH 078/202] Optimize Dockerfile
Just merging RUNs to decrease the number of layers
---
Dockerfile | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 2f438c952..268ec61dc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,9 +7,8 @@ ENV MIX_ENV=prod
RUN apk add git gcc g++ musl-dev make &&\
echo "import Mix.Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
- mix local.rebar --force
-
-RUN mix deps.get --only prod &&\
+ mix local.rebar --force &&\
+ mix deps.get --only prod &&\
mkdir release &&\
mix release --path release
@@ -20,9 +19,8 @@ ARG DATA=/var/lib/pleroma
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
apk update &&\
- apk add ncurses postgresql-client
-
-RUN adduser --system --shell /bin/false --home ${HOME} pleroma &&\
+ apk add ncurses postgresql-client &&\
+ adduser --system --shell /bin/false --home ${HOME} pleroma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
chown -R pleroma ${DATA} &&\
From 04327721d73733c1052d284adca12b949ce61045 Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson
Date: Fri, 2 Aug 2019 23:33:47 -0400
Subject: [PATCH 079/202] Add .dockerignore
---
.dockerignore | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 .dockerignore
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..c5ef89b86
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,12 @@
+.*
+*.md
+AGPL-3
+CC-BY-SA-4.0
+COPYING
+*file
+elixir_buildpack.config
+docs/
+test/
+
+# Required to get version
+!.git
From a187dbb326f8fa3dfe19a113f4db5ed0a95435cb Mon Sep 17 00:00:00 2001
From: Thibaut Girka
Date: Sat, 3 Aug 2019 17:24:57 +0000
Subject: [PATCH 080/202] Add preferredUsername to service actors so Mastodon
can resolve them
---
lib/pleroma/web/activity_pub/views/user_view.ex | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 639519e0a..4a83ac980 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -45,6 +45,7 @@ def render("service.json", %{user: user}) do
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
+ "preferredUsername" => user.nickname,
"name" => "Pleroma",
"summary" =>
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
From 4007717534f9cc880b808b91ba6be5801afb71a0 Mon Sep 17 00:00:00 2001
From: Ashlynn Anderson
Date: Sat, 3 Aug 2019 13:42:57 -0400
Subject: [PATCH 081/202] Run mix format
---
config/docker.exs | 53 ++++++++++++++++++++++++-----------------------
1 file changed, 27 insertions(+), 26 deletions(-)
diff --git a/config/docker.exs b/config/docker.exs
index c07f0b753..63ab4cdee 100644
--- a/config/docker.exs
+++ b/config/docker.exs
@@ -1,8 +1,8 @@
import Config
config :pleroma, Pleroma.Web.Endpoint,
- url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
- http: [ip: {0, 0, 0, 0}, port: 4000]
+ url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
+ http: [ip: {0, 0, 0, 0}, port: 4000]
config :pleroma, :instance,
name: System.get_env("INSTANCE_NAME", "Pleroma"),
@@ -21,8 +21,7 @@
pool_size: 10
# Configure web push notifications
-config :web_push_encryption, :vapid_details,
- subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
+config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
config :pleroma, :database, rum_enabled: false
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
@@ -34,23 +33,24 @@
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
- secret_file = EEx.eval_string(
- """
- import Config
-
- config :pleroma, Pleroma.Web.Endpoint,
- secret_key_base: "<%= secret %>",
- signing_salt: "<%= signing_salt %>"
-
- config :web_push_encryption, :vapid_details,
- public_key: "<%= web_push_public_key %>",
- private_key: "<%= web_push_private_key %>"
- """,
- secret: secret,
- signing_salt: signing_salt,
- web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
- web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
- )
+ secret_file =
+ EEx.eval_string(
+ """
+ import Config
+
+ config :pleroma, Pleroma.Web.Endpoint,
+ secret_key_base: "<%= secret %>",
+ signing_salt: "<%= signing_salt %>"
+
+ config :web_push_encryption, :vapid_details,
+ public_key: "<%= web_push_public_key %>",
+ private_key: "<%= web_push_private_key %>"
+ """,
+ secret: secret,
+ signing_salt: signing_salt,
+ web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
+ web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
+ )
File.write("/var/lib/pleroma/secret.exs", secret_file)
end
@@ -60,8 +60,9 @@
# For additional user config
if File.exists?("/var/lib/pleroma/config.exs"),
do: import_config("/var/lib/pleroma/config.exs"),
- else: File.write("/var/lib/pleroma/config.exs", """
- import Config
-
- # For additional configuration outside of environmental variables
- """)
+ else:
+ File.write("/var/lib/pleroma/config.exs", """
+ import Config
+
+ # For additional configuration outside of environmental variables
+ """)
From 8b2fa31fed1a970c75e077d419dc78be7fc73a93 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Sat, 3 Aug 2019 18:12:38 +0000
Subject: [PATCH 082/202] Handle MRF rejections of incoming AP activities
---
lib/pleroma/web/activity_pub/activity_pub.ex | 3 +++
test/web/federator_test.exs | 16 ++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 07a65127b..2877c029e 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -267,6 +267,9 @@ def create(%{to: to, actor: actor, context: context, object: object} = params, f
else
{:fake, true, activity} ->
{:ok, activity}
+
+ {:error, message} ->
+ {:error, message}
end
end
diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs
index 6e143eee4..73cfaa8f1 100644
--- a/test/web/federator_test.exs
+++ b/test/web/federator_test.exs
@@ -229,5 +229,21 @@ test "rejects incoming AP docs with incorrect origin" do
:error = Federator.incoming_ap_doc(params)
end
+
+ test "it does not crash if MRF rejects the post" do
+ policies = Pleroma.Config.get([:instance, :rewrite_policy])
+ mrf_keyword_policy = Pleroma.Config.get(:mrf_keyword)
+ Pleroma.Config.put([:mrf_keyword, :reject], ["lain"])
+ Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.KeywordPolicy)
+
+ params =
+ File.read!("test/fixtures/mastodon-post-activity.json")
+ |> Poison.decode!()
+
+ assert Federator.incoming_ap_doc(params) == :error
+
+ Pleroma.Config.put([:instance, :rewrite_policy], policies)
+ Pleroma.Config.put(:mrf_keyword, mrf_keyword_policy)
+ end
end
end
From 040347b24820e2773c45a38d4cb6a184d6b14366 Mon Sep 17 00:00:00 2001
From: Sergey Suprunenko
Date: Sat, 3 Aug 2019 18:13:20 +0000
Subject: [PATCH 083/202] Remove spaces from the domain search
---
lib/pleroma/user/search.ex | 2 +-
test/web/mastodon_api/search_controller_test.exs | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index 46620b89a..6fb2c2352 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -44,7 +44,7 @@ defp format_query(query_string) do
query_string = String.trim_leading(query_string, "@")
with [name, domain] <- String.split(query_string, "@"),
- formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:]+/, "") do
+ formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
name <> "@" <> to_string(:idna.encode(formatted_domain))
else
_ -> query_string
diff --git a/test/web/mastodon_api/search_controller_test.exs b/test/web/mastodon_api/search_controller_test.exs
index 043b96c14..49c79ff0a 100644
--- a/test/web/mastodon_api/search_controller_test.exs
+++ b/test/web/mastodon_api/search_controller_test.exs
@@ -95,6 +95,18 @@ test "account search", %{conn: conn} do
assert user_three.nickname in result_ids
end
+
+ test "returns account if query contains a space", %{conn: conn} do
+ user = insert(:user, %{nickname: "shp@shitposter.club"})
+
+ results =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "})
+ |> json_response(200)
+
+ assert length(results) == 1
+ end
end
describe ".search" do
From de0f3b73dd7c76b6b19b38804f98f6e7ccba7222 Mon Sep 17 00:00:00 2001
From: Alexander Strizhakov
Date: Sat, 3 Aug 2019 18:16:09 +0000
Subject: [PATCH 084/202] Admin fixes
---
docs/api/admin_api.md | 4 +++
.../web/admin_api/admin_api_controller.ex | 6 ++--
lib/pleroma/web/admin_api/config.ex | 15 +++++++--
.../admin_api/admin_api_controller_test.exs | 32 +++++++++++++++++++
4 files changed, 52 insertions(+), 5 deletions(-)
diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md
index 22873dde9..7ccb90836 100644
--- a/docs/api/admin_api.md
+++ b/docs/api/admin_api.md
@@ -627,6 +627,9 @@ Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
Keywords can be passed as lists with 2 child tuples, e.g.
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
+If value contains list of settings `[subkey: val1, subkey2: val2, subkey3: val3]`, it's possible to remove only subkeys instead of all settings passing `subkeys` parameter. E.g.:
+{"group": "pleroma", "key": "some_key", "delete": "true", "subkeys": [":subkey", ":subkey3"]}.
+
Compile time settings (need instance reboot):
- all settings by this keys:
- `:hackney_pools`
@@ -645,6 +648,7 @@ Compile time settings (need instance reboot):
- `key` (string or string with leading `:` for atoms)
- `value` (string, [], {} or {"tuple": []})
- `delete` = true (optional, if parameter must be deleted)
+ - `subkeys` [(string with leading `:` for atoms)] (optional, works only if `delete=true` parameter is passed, otherwise will be ignored)
]
- Request (example):
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index fcda57b3e..2d3d0adc4 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -402,9 +402,9 @@ def config_update(conn, %{"configs" => configs}) do
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
updated =
Enum.map(configs, fn
- %{"group" => group, "key" => key, "delete" => "true"} ->
- {:ok, _} = Config.delete(%{group: group, key: key})
- nil
+ %{"group" => group, "key" => key, "delete" => "true"} = params ->
+ {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
+ config
%{"group" => group, "key" => key, "value" => value} ->
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex
index dde05ea7b..a10cc779b 100644
--- a/lib/pleroma/web/admin_api/config.ex
+++ b/lib/pleroma/web/admin_api/config.ex
@@ -55,8 +55,19 @@ def update_or_create(params) do
@spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
def delete(params) do
- with %Config{} = config <- Config.get_by_params(params) do
- Repo.delete(config)
+ with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do
+ if params[:subkeys] do
+ updated_value =
+ Keyword.drop(
+ :erlang.binary_to_term(config.value),
+ Enum.map(params[:subkeys], &do_transform_string(&1))
+ )
+
+ Config.update(config, %{value: updated_value})
+ else
+ Repo.delete(config)
+ {:ok, nil}
+ end
else
nil ->
err =
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index f61499a22..bcbc18639 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1914,6 +1914,38 @@ test "queues key as atom", %{conn: conn} do
]
}
end
+
+ test "delete part of settings by atom subkeys", %{conn: conn} do
+ config =
+ insert(:config,
+ key: "keyaa1",
+ value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
+ )
+
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ group: config.group,
+ key: config.key,
+ subkeys: [":subkey1", ":subkey3"],
+ delete: "true"
+ }
+ ]
+ })
+
+ assert(
+ json_response(conn, 200) == %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "keyaa1",
+ "value" => [%{"tuple" => [":subkey2", "val2"]}]
+ }
+ ]
+ }
+ )
+ end
end
describe "config mix tasks run" do
From 16cfb89240f9f56752ba8d91d84ce81a70f8d6cf Mon Sep 17 00:00:00 2001
From: Thibaut Girka
Date: Sat, 3 Aug 2019 18:28:08 +0000
Subject: [PATCH 085/202] Only add `preferredUsername` to service actor json
when the underlying user actually has a username
---
lib/pleroma/web/activity_pub/views/user_view.ex | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 4a83ac980..8fe38927f 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -45,7 +45,6 @@ def render("service.json", %{user: user}) do
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
- "preferredUsername" => user.nickname,
"name" => "Pleroma",
"summary" =>
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
@@ -58,6 +57,7 @@ def render("service.json", %{user: user}) do
},
"endpoints" => endpoints
}
+ |> Map.merge(if user.nickname == nil do %{} else %{ "preferredUsername" => user.nickname})
|> Map.merge(Utils.make_json_ld_header())
end
From 1fce56c7dffb84917b6943cc5919ed76e06932a5 Mon Sep 17 00:00:00 2001
From: Thibaut Girka
Date: Sat, 3 Aug 2019 18:37:20 +0000
Subject: [PATCH 086/202] Refactor
---
lib/pleroma/web/activity_pub/views/user_view.ex | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 8fe38927f..06c9e1c71 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -57,7 +57,6 @@ def render("service.json", %{user: user}) do
},
"endpoints" => endpoints
}
- |> Map.merge(if user.nickname == nil do %{} else %{ "preferredUsername" => user.nickname})
|> Map.merge(Utils.make_json_ld_header())
end
@@ -66,7 +65,7 @@ def render("user.json", %{user: %User{nickname: nil} = user}),
do: render("service.json", %{user: user})
def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
- do: render("service.json", %{user: user})
+ do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
def render("user.json", %{user: user}) do
{:ok, user} = User.ensure_keys_present(user)
From a035ab8c1d1589a97816d17dac8d60fb4b2275b2 Mon Sep 17 00:00:00 2001
From: "Haelwenn (lanodan) Monnier"
Date: Sat, 3 Aug 2019 22:56:20 +0200
Subject: [PATCH 087/202] templates/layout/app.html.eex: Style anchors
[ci skip]
---
lib/pleroma/web/templates/layout/app.html.eex | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex
index b3cf9ed11..5836ec1e0 100644
--- a/lib/pleroma/web/templates/layout/app.html.eex
+++ b/lib/pleroma/web/templates/layout/app.html.eex
@@ -36,6 +36,11 @@
margin-bottom: 20px;
}
+ a {
+ color: color: #d8a070;
+ text-decoration: none;
+ }
+
form {
width: 100%;
}
From cef3af5536c16ff357fe2e0ed8c560aff16c62de Mon Sep 17 00:00:00 2001
From: Ariadne Conill
Date: Sat, 3 Aug 2019 23:17:17 +0000
Subject: [PATCH 088/202] tasks: relay: add list task
---
CHANGELOG.md | 1 +
lib/mix/tasks/pleroma/relay.ex | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fa9ffd9b..2b0a6189a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -69,6 +69,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- ActivityPub: Optional signing of ActivityPub object fetches.
- Admin API: Endpoint for fetching latest user's statuses
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=` for resending account confirmation.
+- Relays: Added a task to list relay subscriptions.
### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex
index 83ed0ed02..c7324fff6 100644
--- a/lib/mix/tasks/pleroma/relay.ex
+++ b/lib/mix/tasks/pleroma/relay.ex
@@ -5,6 +5,7 @@
defmodule Mix.Tasks.Pleroma.Relay do
use Mix.Task
import Mix.Pleroma
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay
@shortdoc "Manages remote relays"
@@ -22,6 +23,10 @@ defmodule Mix.Tasks.Pleroma.Relay do
``mix pleroma.relay unfollow ``
Example: ``mix pleroma.relay unfollow https://example.org/relay``
+
+ ## List relay subscriptions
+
+ ``mix pleroma.relay list``
"""
def run(["follow", target]) do
start_pleroma()
@@ -44,4 +49,19 @@ def run(["unfollow", target]) do
{:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")
end
end
+
+ def run(["list"]) do
+ start_pleroma()
+
+ with %User{} = user <- Relay.get_actor() do
+ user.following
+ |> Enum.each(fn entry ->
+ URI.parse(entry)
+ |> Map.get(:host)
+ |> shell_info()
+ end)
+ else
+ e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
+ end
+ end
end
From c10a3e035b2761b1d8419d39b8392d499abe9aae Mon Sep 17 00:00:00 2001
From: Pierce McGoran
Date: Sun, 4 Aug 2019 03:01:21 +0000
Subject: [PATCH 089/202] Update link in README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 41d454a03..5aad34ccc 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ If you want to run your own server, feel free to contact us at @lain@pleroma.soy
Currently Pleroma is not packaged by any OS/Distros, but feel free to reach out to us at [#pleroma-dev on freenode](https://webchat.freenode.net/?channels=%23pleroma-dev) or via matrix at for assistance. If you want to change default options in your Pleroma package, please **discuss it with us first**.
### Docker
-While we don’t provide docker files, other people have written very good ones. Take a look at or .
+While we don’t provide docker files, other people have written very good ones. Take a look at or .
### Dependencies
From 9b9453ceaf492ef3af18c12ce67e144a718fd65a Mon Sep 17 00:00:00 2001
From: Pierce McGoran
Date: Sun, 4 Aug 2019 03:12:38 +0000
Subject: [PATCH 090/202] Fix some typos and rework a few lines in
howto_mediaproxy.md
---
docs/config/howto_mediaproxy.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/config/howto_mediaproxy.md b/docs/config/howto_mediaproxy.md
index ed70c3ed4..16c40c5db 100644
--- a/docs/config/howto_mediaproxy.md
+++ b/docs/config/howto_mediaproxy.md
@@ -1,8 +1,8 @@
# How to activate mediaproxy
## Explanation
-Without the `mediaproxy` function, Pleroma don't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
-With the `mediaproxy` function you can use the cache ability of nginx, to cache these content, so user can access it faster, cause it's loaded from your server.
+Without the `mediaproxy` function, Pleroma doesn't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
+With the `mediaproxy` function you can use nginx to cache this content, so users can access it faster, because it's loaded from your server.
## Activate it
From 39c7bbe18fcbff608bbc0b72ec6134872a1c946a Mon Sep 17 00:00:00 2001
From: Hakaba Hitoyo
Date: Sun, 4 Aug 2019 04:32:45 +0000
Subject: [PATCH 091/202] Remove longfox emoji set
---
CHANGELOG.md | 3 +++
config/emoji.txt | 28 ----------------------------
priv/static/emoji/f_00b.png | Bin 371 -> 0 bytes
priv/static/emoji/f_00b11b.png | Bin 661 -> 0 bytes
priv/static/emoji/f_00b33b.png | Bin 662 -> 0 bytes
priv/static/emoji/f_00h.png | Bin 7522 -> 0 bytes
priv/static/emoji/f_00t.png | Bin 541 -> 0 bytes
priv/static/emoji/f_01b.png | Bin 4510 -> 0 bytes
priv/static/emoji/f_03b.png | Bin 2872 -> 0 bytes
priv/static/emoji/f_10b.png | Bin 2849 -> 0 bytes
priv/static/emoji/f_11b.png | Bin 447 -> 0 bytes
priv/static/emoji/f_11b00b.png | Bin 615 -> 0 bytes
priv/static/emoji/f_11b22b.png | Bin 618 -> 0 bytes
priv/static/emoji/f_11h.png | Bin 7314 -> 0 bytes
priv/static/emoji/f_11t.png | Bin 559 -> 0 bytes
priv/static/emoji/f_12b.png | Bin 4352 -> 0 bytes
priv/static/emoji/f_21b.png | Bin 2900 -> 0 bytes
priv/static/emoji/f_22b.png | Bin 386 -> 0 bytes
priv/static/emoji/f_22b11b.png | Bin 666 -> 0 bytes
priv/static/emoji/f_22b33b.png | Bin 663 -> 0 bytes
priv/static/emoji/f_22h.png | Bin 7448 -> 0 bytes
priv/static/emoji/f_22t.png | Bin 549 -> 0 bytes
priv/static/emoji/f_23b.png | Bin 4334 -> 0 bytes
priv/static/emoji/f_30b.png | Bin 4379 -> 0 bytes
priv/static/emoji/f_32b.png | Bin 2921 -> 0 bytes
priv/static/emoji/f_33b.png | Bin 459 -> 0 bytes
priv/static/emoji/f_33b00b.png | Bin 611 -> 0 bytes
priv/static/emoji/f_33b22b.png | Bin 623 -> 0 bytes
priv/static/emoji/f_33h.png | Bin 7246 -> 0 bytes
priv/static/emoji/f_33t.png | Bin 563 -> 0 bytes
30 files changed, 3 insertions(+), 28 deletions(-)
delete mode 100644 priv/static/emoji/f_00b.png
delete mode 100644 priv/static/emoji/f_00b11b.png
delete mode 100644 priv/static/emoji/f_00b33b.png
delete mode 100644 priv/static/emoji/f_00h.png
delete mode 100644 priv/static/emoji/f_00t.png
delete mode 100644 priv/static/emoji/f_01b.png
delete mode 100644 priv/static/emoji/f_03b.png
delete mode 100644 priv/static/emoji/f_10b.png
delete mode 100644 priv/static/emoji/f_11b.png
delete mode 100644 priv/static/emoji/f_11b00b.png
delete mode 100644 priv/static/emoji/f_11b22b.png
delete mode 100644 priv/static/emoji/f_11h.png
delete mode 100644 priv/static/emoji/f_11t.png
delete mode 100644 priv/static/emoji/f_12b.png
delete mode 100644 priv/static/emoji/f_21b.png
delete mode 100644 priv/static/emoji/f_22b.png
delete mode 100644 priv/static/emoji/f_22b11b.png
delete mode 100644 priv/static/emoji/f_22b33b.png
delete mode 100644 priv/static/emoji/f_22h.png
delete mode 100644 priv/static/emoji/f_22t.png
delete mode 100644 priv/static/emoji/f_23b.png
delete mode 100644 priv/static/emoji/f_30b.png
delete mode 100644 priv/static/emoji/f_32b.png
delete mode 100644 priv/static/emoji/f_33b.png
delete mode 100644 priv/static/emoji/f_33b00b.png
delete mode 100644 priv/static/emoji/f_33b22b.png
delete mode 100644 priv/static/emoji/f_33h.png
delete mode 100644 priv/static/emoji/f_33t.png
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fa9ffd9b..fc4d08aaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -76,6 +76,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- RichMedia: parsers and their order are configured in `rich_media` config.
- RichMedia: add the rich media ttl based on image expiration time.
+### Removed
+- Emoji: Remove longfox emojis.
+
## [1.0.1] - 2019-07-14
### Security
- OStatus: fix an object spoofing vulnerability.
diff --git a/config/emoji.txt b/config/emoji.txt
index 79246f239..200768ad1 100644
--- a/config/emoji.txt
+++ b/config/emoji.txt
@@ -1,30 +1,2 @@
firefox, /emoji/Firefox.gif, Gif,Fun
blank, /emoji/blank.png, Fun
-f_00b, /emoji/f_00b.png
-f_00b11b, /emoji/f_00b11b.png
-f_00b33b, /emoji/f_00b33b.png
-f_00h, /emoji/f_00h.png
-f_00t, /emoji/f_00t.png
-f_01b, /emoji/f_01b.png
-f_03b, /emoji/f_03b.png
-f_10b, /emoji/f_10b.png
-f_11b, /emoji/f_11b.png
-f_11b00b, /emoji/f_11b00b.png
-f_11b22b, /emoji/f_11b22b.png
-f_11h, /emoji/f_11h.png
-f_11t, /emoji/f_11t.png
-f_12b, /emoji/f_12b.png
-f_21b, /emoji/f_21b.png
-f_22b, /emoji/f_22b.png
-f_22b11b, /emoji/f_22b11b.png
-f_22b33b, /emoji/f_22b33b.png
-f_22h, /emoji/f_22h.png
-f_22t, /emoji/f_22t.png
-f_23b, /emoji/f_23b.png
-f_30b, /emoji/f_30b.png
-f_32b, /emoji/f_32b.png
-f_33b, /emoji/f_33b.png
-f_33b00b, /emoji/f_33b00b.png
-f_33b22b, /emoji/f_33b22b.png
-f_33h, /emoji/f_33h.png
-f_33t, /emoji/f_33t.png
diff --git a/priv/static/emoji/f_00b.png b/priv/static/emoji/f_00b.png
deleted file mode 100644
index 3d00b89b02acbcf8cd3b4ff388e2f09f06c0aee1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 371
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV6^gdaSW+oe0!bI&BamX_{aZe
zX6tr}a(3}uiBn;ST_nxo^!x?ST;&xR;T&I=Y5KF@5L*AU=Eu*^3^kJaKZ=3oU;`6w
zt-JN@_OS)(J9FoTt==ppyU6XFv-Q@zto0Qdv!5@2A)YF8{Drt>8YiOy14{#g00WZ)
z0|x^mE>6Ry;sTMsi)`-2?%!(d_nO`LcJru6{1-oD!Mef_(Y8l8#ae0+Qj4GnKrsQ0BxFf%h37Z<-?u0A16
z0%&7iUS4i)Zd_bkLPElu8)u5{!(D1qB6}nVG)6KHlD*-rioHKD_<#?#+{POCFw_
z{qWS>hbQMeKD+2luf*|Y=HtyQr@90$%rZJNQStnAoeQ&!pRKZZvPkXO61C?m4Odqy
zFDsK-P$bR6!;_w#o}ZszQBjeeo_cp*b52f9R%XV>_irB@={wym`gWb=lx$fcA))H(
z>TJ8SnLw8@mIV0)GdMiEkp^U2d%8G=R4~3d7hKfBD8TSA^7h8u+uKV2Z+>FA`PXu_
zd+(S_YLj$D`dpGryVWPH+_lvB=A0d;+m$yu-R)Xhv@|bw<~P2=>o+AXT)oS2;W9VS
zcr0Ko?~B*+5;?gCE_^ugqMzy5F?F@2t5&|04CXuV=FXp{UB3FKE`93KR8MeK7fkkZ
za8X|!812_^Z|$k5gVTfq0*=PK`$wDhZOlBy8L)kA?#-Kbji0itxczRQ
zjLfYfrZ4Y2`n0#rY3R~oMbTJPly#cZB6#lUqf2$wA9$%>7X~W&+3(A9;>FoTn(7Xv
z)#}&gFch*wHJ!L`&SEn~no&*e=-n^DPb$~ueVl&%@vgml^-XkS4%;yBPFPsT{N%@E
OkaAB~KbLh*2~7Y>h#0s4
diff --git a/priv/static/emoji/f_00b33b.png b/priv/static/emoji/f_00b33b.png
deleted file mode 100644
index 8f4929297e401d8fc0b150c0080b989d974e00db..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 662
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
diff --git a/priv/static/emoji/f_00t.png b/priv/static/emoji/f_00t.png
deleted file mode 100644
index 31d98b4333b253da7c08b7925928d3750b816125..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 541
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
diff --git a/priv/static/emoji/f_03b.png b/priv/static/emoji/f_03b.png
deleted file mode 100644
index 9e4ff1bf77074361c97d1ef8fcdc205575d03916..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2872
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
Wnd|l2N8F|W00003RzX=`6xmTY)Ni*_)
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
diff --git a/priv/static/emoji/f_11b00b.png b/priv/static/emoji/f_11b00b.png
deleted file mode 100644
index c4c30e11f5c16d81c05217d2a109d27d89a73e4d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 615
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fsrG?C&U%V&CSjA_4WDx|No~C
zZ$G?y^X>2dC+C)&>6N%J%jm)^<7cZZR#z+Y@bDBD7stiLd3$?)eE;VC+gD%z{(pFK
z_VH%sGZPihOjLTZNNrh}4A8Xn^z?*;1aEJzho|NqZ)VBN%#4bP3J3@Y3=GUjPfbrx
z&C1O9@b2};_iyhX>b|?b<=+0*yZf3S9O*mR!FQ~Y;aDT%>2A^U({b4j%g!Y6&P4IPREY^`68&isQ?g~Hq@=jHxp{ec`S|#lnVA_G8HI#|
z+S=M`YisN4>l+#x^78Tu3JP*^a;mGVpDj^aP$d2E*?YcQo;DgTe~DWM4f2cZE%
diff --git a/priv/static/emoji/f_11b22b.png b/priv/static/emoji/f_11b22b.png
deleted file mode 100644
index 47425e06e2713254552d9081069ed7553f6181e6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 618
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fsrG?C&U%V&CJa7_V)V!|No~C
zZ$G?y{q67n$7dIv>JmIZUFZCC-RCO}7ZgeJ@$pquRK&%_eSH7s{o7Yx|Neh?a?bH)
z<}(u&&rDQ$wnPnRPJVuVLPEmBQ*)0uvoJC;78De;wYAmO*4Ee8H#9U97Z>N{<>ln$
zR99C^Nl9^YbMx}@GBYy^2?=#3iFYQ7_oYfqNR#MKlbDh%`);lA+m#ycR%*Oiq5gWg
z`kUqIZ`WB~m}PvjgYQ@)!?8xj)7_%?4|U(&-*Rt%>)n0L503PGc=zVx`?neCsp;vd
zS(zD8QBeT_0fB*mPZp^yE0gi|_I!A9c6xexZf>rxug{ZnOV0F4T$p9_Y?a08YGocC
z9wpI7o^R1%uzC3;cb(>KnV&Jc({El|VyP>`{b5yL
zO8V3$P^`g0LrJ3cx_w@nA_>(OH8!n#mAreMhzwj7#8|Q`IOUtq#--7&Y0GD>-SNwP
z^@hjd@)60E_vc^p2#?$U?T!G7dIaZB{ef?OJN4|RypB6OiEH=9U17<#ORk-l%6wY3
aHu3)RS+gW`A6wr7srPjCb6Mw<&;$S)^#2(E
diff --git a/priv/static/emoji/f_11h.png b/priv/static/emoji/f_11h.png
deleted file mode 100644
index 28342363a20b6dbdeb34a2fc4d30a9def0c3201f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 7314
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
diff --git a/priv/static/emoji/f_11t.png b/priv/static/emoji/f_11t.png
deleted file mode 100644
index dca67dc70ac3d8259ab7a62acea3d9298751abb6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 559
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&H3s;ExB}^fgamJIuWx_AKHX8uIb+Ra8`*4}EtCXeDDwkY6x^!?PP{Kz59$i(^Oy
zgGn3nCI%jj7yPUO32_(CH#Bj(JZw-%5Ln=%AT^s8Xk(I5gQeJo9tQoF`i2VJ0S#UM
zf|$>2Y)E6NaID|S_^kUKKf4LjgN8~@4guS^NTAvRrvotzix^&hXT1qM2Ur>mdKI;Vst0M7f_6#xJL
diff --git a/priv/static/emoji/f_12b.png b/priv/static/emoji/f_12b.png
deleted file mode 100644
index 9925adb7cf6dc3af9218a7e2f258840c4df7b423..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 4352
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
GgeCxGV1L2@
diff --git a/priv/static/emoji/f_22b11b.png b/priv/static/emoji/f_22b11b.png
deleted file mode 100644
index 4bdfb3107c69afa25b605a62a11f18e05155fe9b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 666
zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fsrG?C&U%Vjf;!R%FOux|NqDL
zZ$G?y^X>2d2S@r&cZ*(_W&C!X<&gs@ifb{g#5AR;TfBWj|-~V^_H6Lqa
zI5Sb{&2shrGzp+NIXO9jfq@z6srUA`9&2O-f`WpA;^N};^z_Wk%>4ZPii!#z9v(hE
zzSY&r%gSUH6iGi@W$|Q@+Os8U&sQ3rpRRjhmeHAsisz^6oavP~-pqWwndMZM;FEJp
z9-f^2@YLLgC+9pqyXe!0w;$iX@%8ob_V)Dl_R7u8O-M+1y+A3CZ{gDVK&-h$X`|rSJR_BK&HoGQ&Y?{8tt&ik5e-xey#csp;?JFM)z3^+I@ZW=cVF|@
zzyIIAef8np>-6;0fPjF4f&xZH#$%0)_x87Dq^AZ323Ay5;PGbWD
z-mFk>XlUq66o0o;qrSeLmzTFQN&M|fjoR8;Zf@?o`&-)D+N7kU-mNu0*}-@JPR
zRD99qzd-LXmIV0)GdMiEkp^Vjdb&7s*6nS_=iM;-zJF%S|M<=G
zx<1bnm63e>nZ0S@%0m+~a#mF+%@o>PqBi@qf2hW^Q^BD^S7*(*D!rNM?6orvyPX>t
z7@1f&1O(RRt$(+H^_N!j!X6`D{bYs3pFQn%_cZET#Xr4W-fsB8`T6ocr>uJ#_xN={
zbSOB0wLbYS50rI(zW&ee{ZCSPbXNX}pEmWXseW>TZ)&acBGq=ofJ>cTOWzoAalAK7Tdy5K}Va0fs7rr>mdKI;Vst0HhxxN&o-=
diff --git a/priv/static/emoji/f_22h.png b/priv/static/emoji/f_22h.png
deleted file mode 100644
index 3b27e2de8cd26a53d5e84c4f8e3ce7c469bb7abb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 7448
zcmV+z9p~bSP)CX0dqyn}FTJNj+gwr$(CZQHhO+qP|^wr)>P
z);8`r--({muNwcd@+Y@n&_0reQ)T+sloM%5L(Zfnck-ejiXaokr$m&T(oj0eOj#%g
z<^GyNR9IfWrU>Q#TAN&yRqdaiQd2TYNH&V3VDct6($eo;Ho8tbXf_R|PSlhdPz&lo
zQ)xfJ-_sd|snc|i?$T}b{R?_c9|_;b!Ql%yJ2@zH8V4A4jxcB)$PsP^GODjC
zcYb!&J3_Cv5gO;%{*DeZ=J)zUZ|J$YuFG_W4$xLwNi%3P^`z!hjqD)ngv}d_{Xhp1pTvXP|ZN}
ztr~#7)dJ9`sz3Ty55TZGff(B`2oswGV_J(4%xV>ixotzSs6!Z*b_&CaF5y_!Egb86
zMkqG*iNw}^k=W9gue5Vu6n1^fZrU**%HBp^H}s0_zq)(4GNz2ZuzeWjvStVb%%4x{IU*NkF4!6J>^C`
zUucsJk(G7yo
zs-!QByftM{;)*BpZ5;Eb=RLp>>>V7Xw7)&miVE30;{r91lB$YQA3CkoYLF*)&bT&X
z$wXhTSYiV8ZmN;{e>I=k$Q2KH&1<)tLBYdtU(^
z)wO