diff options
author | Egor Kislitsyn <egor@kislitsyn.com> | 2020-03-03 00:32:34 +0400 |
---|---|---|
committer | Egor Kislitsyn <egor@kislitsyn.com> | 2020-03-03 00:32:34 +0400 |
commit | 0f386110c6e15148ff1ff5ea3451885485fcb7ff (patch) | |
tree | 8a4dcbede6c674730980e1fafa5b956518170a14 /lib | |
parent | 011ede45361096f55dda938078e24574cdf33b2b (diff) | |
parent | 4c02e049358441529c54a72cd11f1c81ee897d49 (diff) | |
download | pleroma-0f386110c6e15148ff1ff5ea3451885485fcb7ff.tar.gz |
Merge remote-tracking branch 'origin/develop' into global-status-expiration
Diffstat (limited to 'lib')
115 files changed, 1320 insertions, 373 deletions
diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 3e76d2c97..5c9ef6904 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Config do diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 24d999707..2b03a3009 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Emoji do @@ -186,11 +186,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}") - {:ok, _} = - :zip.unzip( - binary_archive, - cwd: tmp_pack_dir - ) + {:ok, _} = :zip.unzip(binary_archive, cwd: String.to_charlist(tmp_pack_dir)) emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 9af6cda30..bc842a59f 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,11 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Instance do use Mix.Task import Mix.Pleroma + alias Pleroma.Config + @shortdoc "Manages Pleroma instance" @moduledoc File.read!("docs/administration/CLI_tasks/instance.md") @@ -63,7 +65,8 @@ defmodule Mix.Tasks.Pleroma.Instance do get_option( options, :instance_name, - "What is the name of your instance? (e.g. Pleroma/Soykaf)" + "What is the name of your instance? (e.g. The Corndog Emporium)", + domain ) email = get_option(options, :admin_email, "What is your admin email address?") @@ -153,6 +156,8 @@ defmodule Mix.Tasks.Pleroma.Instance do Pleroma.Config.get([:instance, :static_dir]) ) + Config.put([:instance, :static_dir], static_dir) + 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) @@ -202,8 +207,14 @@ defmodule Mix.Tasks.Pleroma.Instance do write_robots_txt(indexable, template_dir) shell_info( - "\n All files successfully written! Refer to the installation instructions for your platform for next steps" + "\n All files successfully written! Refer to the installation instructions for your platform for next steps." ) + + if db_configurable? do + shell_info( + " Please transfer your config to the database after running database migrations. Refer to \"Transfering the config to/from the database\" section of the docs for more information." + ) + end else shell_error( "The task would have overwritten the following files:\n" <> diff --git a/lib/mix/tasks/pleroma/refresh_counter_cache.ex b/lib/mix/tasks/pleroma/refresh_counter_cache.ex new file mode 100644 index 000000000..15b4dbfa6 --- /dev/null +++ b/lib/mix/tasks/pleroma/refresh_counter_cache.ex @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.RefreshCounterCache do + @shortdoc "Refreshes counter cache" + + use Mix.Task + + alias Pleroma.Activity + alias Pleroma.CounterCache + alias Pleroma.Repo + + require Logger + import Ecto.Query + + def run([]) do + Mix.Pleroma.start_pleroma() + + ["public", "unlisted", "private", "direct"] + |> Enum.each(fn visibility -> + count = status_visibility_count_query(visibility) + name = "status_visibility_#{visibility}" + CounterCache.set(name, count) + Mix.Pleroma.shell_info("Set #{name} to #{count}") + end) + + Mix.Pleroma.shell_info("Done") + end + + defp status_visibility_count_query(visibility) do + Activity + |> where( + [a], + fragment( + "activity_visibility(?, ?, ?) = ?", + a.actor, + a.recipients, + a.data, + ^visibility + ) + ) + |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data)) + |> Repo.aggregate(:count, :id, timeout: :timer.minutes(30)) + end +end diff --git a/lib/mix/tasks/pleroma/robotstxt.ex b/lib/mix/tasks/pleroma/robotstxt.ex index e99dd8502..24f08180e 100644 --- a/lib/mix/tasks/pleroma/robotstxt.ex +++ b/lib/mix/tasks/pleroma/robotstxt.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RobotsTxt do diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 85c9e4954..40dd9bdc0 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.User do @@ -100,8 +100,7 @@ defmodule Mix.Tasks.Pleroma.User do User.perform(:delete, user) shell_info("User #{nickname} deleted.") else - _ -> - shell_error("No local user #{nickname}") + _ -> shell_error("No local user #{nickname}") end end diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 72e2256ea..397eb6e3f 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity do @@ -310,7 +310,7 @@ defmodule Pleroma.Activity do def restrict_deactivated_users(query) do deactivated_users = - from(u in User.Query.build(deactivated: true), select: u.ap_id) + from(u in User.Query.build(%{deactivated: true}), select: u.ap_id) |> Repo.all() Activity.Queries.exclude_authors(query, deactivated_users) diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index 79f305201..04593b9fb 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Queries do @@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do Contains queries for Activity. """ - import Ecto.Query, only: [from: 2] + import Ecto.Query, only: [from: 2, where: 3] @type query :: Ecto.Queryable.t() | Activity.t() @@ -30,7 +30,7 @@ defmodule Pleroma.Activity.Queries do ) end - @spec by_author(query, String.t()) :: query + @spec by_author(query, User.t()) :: query def by_author(query \\ Activity, %User{ap_id: ap_id}) do from(a in query, where: a.actor == ^ap_id) end @@ -63,6 +63,22 @@ defmodule Pleroma.Activity.Queries do ) end + @spec by_object_in_reply_to_id(query, String.t(), keyword()) :: query + def by_object_in_reply_to_id(query, in_reply_to_id, opts \\ []) do + query = + if opts[:skip_preloading] do + Activity.with_joined_object(query) + else + Activity.with_preloaded_object(query) + end + + where( + query, + [activity, object: o], + fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(in_reply_to_id)) + ) + end + @spec by_type(query, String.t()) :: query def by_type(query \\ Activity, activity_type) do from( diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index f96e208da..ceb365bb3 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Search do diff --git a/lib/pleroma/activity_expiration.ex b/lib/pleroma/activity_expiration.ex index 7ea5c48ca..db9c88d84 100644 --- a/lib/pleroma/activity_expiration.ex +++ b/lib/pleroma/activity_expiration.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ActivityExpiration do @@ -62,6 +62,6 @@ defmodule Pleroma.ActivityExpiration do def expires_late_enough?(scheduled_at) do now = NaiveDateTime.utc_now() diff = NaiveDateTime.diff(scheduled_at, now, :millisecond) - diff >= @min_activity_lifetime + diff > @min_activity_lifetime end end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 27758cf94..18854b850 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Application do diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha/captcha.ex index c2765a5b8..cf75c3adc 100644 --- a/lib/pleroma/captcha/captcha.ex +++ b/lib/pleroma/captcha/captcha.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha do @@ -50,7 +50,7 @@ defmodule Pleroma.Captcha do token = new_captcha[:token] secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt") sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign") - # Basicallty copy what Phoenix.Token does here, add the time to + # Basically copy what Phoenix.Token does here, add the time to # the actual data and make it a binary to then encrypt it encrypted_captcha_answer = %{ @@ -62,7 +62,7 @@ defmodule Pleroma.Captcha do { :reply, - # Repalce the answer with the encrypted answer + # Replace the answer with the encrypted answer %{new_captcha | answer_data: encrypted_captcha_answer}, state } @@ -82,7 +82,8 @@ defmodule Pleroma.Captcha do valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid) result = - with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret), + with false <- is_nil(answer_data), + {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret), %{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do try do if DateTime.before?(at, valid_if_after), diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex index 5306fe1aa..06c479ca9 100644 --- a/lib/pleroma/captcha/native.ex +++ b/lib/pleroma/captcha/native.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Native do @@ -10,8 +10,8 @@ defmodule Pleroma.Captcha.Native do @impl Service def new do case Captcha.get() do - {:timeout} -> - %{error: dgettext("errors", "Captcha timeout")} + :error -> + %{error: dgettext("errors", "Captcha error")} {:ok, answer_data, img_binary} -> %{ diff --git a/lib/pleroma/config/config_db.ex b/lib/pleroma/config/config_db.ex index 119251bee..2b43d4c36 100644 --- a/lib/pleroma/config/config_db.ex +++ b/lib/pleroma/config/config_db.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConfigDB do diff --git a/lib/pleroma/config/holder.ex b/lib/pleroma/config/holder.ex index d4fe892af..f1a339703 100644 --- a/lib/pleroma/config/holder.ex +++ b/lib/pleroma/config/holder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Holder do diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index b8787cb49..df2d18725 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Loader do diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 6c5ba1f95..1846aa22c 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.TransferTask do @@ -146,9 +146,7 @@ defmodule Pleroma.Config.TransferTask do defp update_env(group, key, nil), do: Application.delete_env(group, key) defp update_env(group, key, value), do: Application.put_env(group, key, value) - defp restart(_, :pleroma, :test), do: Logger.warn("pleroma restarted") - - defp restart(_, :pleroma, _), do: send(Restarter.Pleroma, :after_boot) + defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env) defp restart(started_applications, app, _) do with {^app, _, _} <- List.keyfind(started_applications, app, 0), diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index e5d28ebff..693825cf5 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.Participation do @@ -133,10 +133,8 @@ defmodule Pleroma.Conversation.Participation do [user.id | user_ids] |> Enum.uniq() |> Enum.reduce([], fn user_id, acc -> - case FlakeId.Ecto.CompatType.dump(user_id) do - {:ok, user_id} -> [user_id | acc] - _ -> acc - end + {:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id) + [user_id | acc] end) conversation_subquery = diff --git a/lib/pleroma/counter_cache.ex b/lib/pleroma/counter_cache.ex new file mode 100644 index 000000000..4d348a413 --- /dev/null +++ b/lib/pleroma/counter_cache.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.CounterCache do + alias Pleroma.CounterCache + alias Pleroma.Repo + use Ecto.Schema + import Ecto.Changeset + import Ecto.Query + + schema "counter_cache" do + field(:name, :string) + field(:count, :integer) + end + + def changeset(struct, params) do + struct + |> cast(params, [:name, :count]) + |> validate_required([:name]) + |> unique_constraint(:name) + end + + def get_as_map(names) when is_list(names) do + CounterCache + |> where([cc], cc.name in ^names) + |> Repo.all() + |> Enum.group_by(& &1.name, & &1.count) + |> Map.new(fn {k, v} -> {k, hd(v)} end) + end + + def set(name, count) do + %CounterCache{} + |> changeset(%{"name" => name, "count" => count}) + |> Repo.insert( + on_conflict: [set: [count: count]], + returning: true, + conflict_target: :name + ) + end +end diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index 5f23345f7..55f61024e 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.AdminEmail do diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex new file mode 100644 index 000000000..7d16b807f --- /dev/null +++ b/lib/pleroma/emails/new_users_digest_email.ex @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emails.NewUsersDigestEmail do + use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email_styled} + + defp instance_notify_email do + Pleroma.Config.get([:instance, :notify_email]) || Pleroma.Config.get([:instance, :email]) + end + + def new_users(to, users_and_statuses) do + instance_name = Pleroma.Config.get([:instance, :name]) + styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling]) + + logo_url = + Pleroma.Web.Endpoint.url() <> + Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo]) + + new() + |> to({to.name, to.email}) + |> from({instance_name, instance_notify_email()}) + |> subject("#{instance_name} New Users") + |> render_body("new_users_digest.html", %{ + title: "New Users", + users_and_statuses: users_and_statuses, + instance: instance_name, + styling: styling, + logo_url: logo_url + }) + end +end diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index b8cb3bf03..a6d281151 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FollowingRelationship do diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 90895374d..e2a658cb3 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Formatter do diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 11513106e..d78c5f202 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTML do @@ -108,6 +108,7 @@ defmodule Pleroma.HTML do Cachex.fetch!(:scrubber_cache, key, fn _key -> result = content + |> Floki.parse_fragment!() |> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]") |> Floki.attribute("a", "href") |> Enum.at(0) diff --git a/lib/pleroma/mime.ex b/lib/pleroma/mime.ex index 36771533f..6ee055f50 100644 --- a/lib/pleroma/mime.ex +++ b/lib/pleroma/mime.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MIME do @@ -9,7 +9,7 @@ defmodule Pleroma.MIME do @default "application/octet-stream" @read_bytes 35 - @spec file_mime_type(String.t()) :: + @spec file_mime_type(String.t(), String.t()) :: {:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error def file_mime_type(path, filename) do with {:ok, content_type} <- file_mime_type(path), diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 66e91fcef..60dba3434 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Notification do diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 52556bf31..9574432f0 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -1,10 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object do use Ecto.Schema + import Ecto.Query + import Ecto.Changeset + alias Pleroma.Activity alias Pleroma.Object alias Pleroma.Object.Fetcher @@ -12,9 +15,6 @@ defmodule Pleroma.Object do alias Pleroma.Repo alias Pleroma.User - import Ecto.Query - import Ecto.Changeset - require Logger @type t() :: %__MODULE__{} @@ -145,18 +145,18 @@ defmodule Pleroma.Object do # Legacy objects can be mutated by anybody def authorize_mutation(%Object{}, %User{}), do: true + @spec get_cached_by_ap_id(String.t()) :: Object.t() | nil def get_cached_by_ap_id(ap_id) do key = "object:#{ap_id}" - Cachex.fetch!(:object_cache, key, fn _ -> - object = get_by_ap_id(ap_id) - - if object do - {:commit, object} - else - {:ignore, object} - end - end) + with {:ok, nil} <- Cachex.get(:object_cache, key), + object when not is_nil(object) <- get_by_ap_id(ap_id), + {:ok, true} <- Cachex.put(:object_cache, key, object) do + object + else + {:ok, object} -> object + nil -> nil + end end def context_mapping(context) do @@ -301,4 +301,26 @@ defmodule Pleroma.Object do def local?(%Object{data: %{"id" => id}}) do String.starts_with?(id, Pleroma.Web.base_url() <> "/") end + + def replies(object, opts \\ []) do + object = Object.normalize(object) + + query = + Object + |> where( + [o], + fragment("(?)->>'inReplyTo' = ?", o.data, ^object.data["id"]) + ) + |> order_by([o], asc: o.id) + + if opts[:self_only] do + actor = object.data["actor"] + where(query, [o], fragment("(?)->>'actor' = ?", o.data, ^actor)) + else + query + end + end + + def self_replies(object, opts \\ []), + do: replies(object, Keyword.put(opts, :self_only, true)) end diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 25aa32f60..9ae6a5600 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Containment do @@ -39,15 +39,8 @@ defmodule Pleroma.Object.Containment do defp compare_uris(_, %URI{scheme: "tag"}), do: :ok end - defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do - if id_uri.host == other_uri.host do - :ok - else - :error - end - end - - defp compare_uris(_, _), do: :error + defp compare_uris(%URI{host: host} = _id_uri, %URI{host: host} = _other_uri), do: :ok + defp compare_uris(_id_uri, _other_uri), do: :error @doc """ Checks that an imported AP object's actor matches the domain it came from. diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 037c42339..eaa13d1e7 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Fetcher do @@ -10,6 +10,7 @@ defmodule Pleroma.Object.Fetcher do alias Pleroma.Signature alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.Federator require Logger require Pleroma.Constants @@ -59,20 +60,23 @@ defmodule Pleroma.Object.Fetcher do end end - # TODO: - # This will create a Create activity, which we need internally at the moment. + # Note: will create a Create activity, which we need internally at the moment. def fetch_object_from_id(id, options \\ []) do - with {:fetch_object, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)}, - {:fetch, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)}, - {:normalize, nil} <- {:normalize, Object.normalize(data, false)}, + with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)}, + {_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])}, + {_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)}, + {_, nil} <- {:normalize, Object.normalize(data, false)}, params <- prepare_activity_params(data), - {:containment, :ok} <- {:containment, Containment.contain_origin(id, params)}, - {:transmogrifier, {:ok, activity}} <- + {_, :ok} <- {:containment, Containment.contain_origin(id, params)}, + {_, {:ok, activity}} <- {:transmogrifier, Transmogrifier.handle_incoming(params, options)}, - {:object, _data, %Object{} = object} <- + {_, _data, %Object{} = object} <- {:object, data, Object.normalize(activity, false)} do {:ok, object} else + {:allowed_depth, false} -> + {:error, "Max thread distance exceeded."} + {:containment, _} -> {:error, "Object containment failed."} diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 4535ca7c5..d43a96cd2 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Pagination do @@ -12,11 +12,15 @@ defmodule Pleroma.Pagination do alias Pleroma.Repo + @type type :: :keyset | :offset + @default_limit 20 + @max_limit 40 @page_keys ["max_id", "min_id", "limit", "since_id", "order"] def page_keys, do: @page_keys + @spec fetch_paginated(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()] def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil) def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do @@ -57,6 +61,7 @@ defmodule Pleroma.Pagination do |> Repo.all() end + @spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()] def paginate(query, options, method \\ :keyset, table_binding \\ nil) def paginate(query, options, :keyset, table_binding) do @@ -130,7 +135,11 @@ defmodule Pleroma.Pagination do end defp restrict(query, :limit, options, _table_binding) do - limit = Map.get(options, :limit, @default_limit) + limit = + case Map.get(options, :limit, @default_limit) do + limit when limit < @max_limit -> limit + _ -> @max_limit + end query |> limit(^limit) diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index b04273979..81e6b4f2a 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.HTTPSecurityPlug do diff --git a/lib/pleroma/plugs/http_signature.ex b/lib/pleroma/plugs/http_signature.ex index 23d22a712..036e2a773 100644 --- a/lib/pleroma/plugs/http_signature.ex +++ b/lib/pleroma/plugs/http_signature.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do import Plug.Conn + import Phoenix.Controller, only: [get_format: 1, text: 2] require Logger def init(options) do @@ -15,25 +16,27 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do end def call(conn, _opts) do - headers = get_req_header(conn, "signature") - signature = Enum.at(headers, 0) + if get_format(conn) == "activity+json" do + conn + |> maybe_assign_valid_signature() + |> maybe_require_signature() + else + conn + end + end - if signature do + defp maybe_assign_valid_signature(conn) do + if has_signature_header?(conn) do # set (request-target) header to the appropriate value # we also replace the digest header with the one we computed - conn = - conn - |> put_req_header( - "(request-target)", - String.downcase("#{conn.method}") <> " #{conn.request_path}" - ) + request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}" conn = - if conn.assigns[:digest] do - conn - |> put_req_header("digest", conn.assigns[:digest]) - else - conn + conn + |> put_req_header("(request-target)", request_target) + |> case do + %{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest) + conn -> conn end assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn)) @@ -42,4 +45,21 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do conn end end + + defp has_signature_header?(conn) do + conn |> get_req_header("signature") |> Enum.at(0, false) + end + + defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn + + defp maybe_require_signature(conn) do + if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do + conn + |> put_status(:unauthorized) + |> text("Request not signed") + |> halt() + else + conn + end + end end diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 07c0f7fdb..38df074ad 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.OAuthScopesPlug do diff --git a/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex index 187582ede..884268d96 100644 --- a/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex +++ b/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex @@ -7,8 +7,8 @@ defmodule Pleroma.Plugs.RateLimiter.LimiterSupervisor do DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__) end - def add_limiter(limiter_name, expiration) do - {:ok, _pid} = + def add_or_return_limiter(limiter_name, expiration) do + result = DynamicSupervisor.start_child( __MODULE__, %{ @@ -28,6 +28,12 @@ defmodule Pleroma.Plugs.RateLimiter.LimiterSupervisor do ]} } ) + + case result do + {:ok, _pid} = result -> result + {:error, {:already_started, pid}} -> {:ok, pid} + _ -> result + end end @impl true diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex index 7fb92489c..c3f6351c8 100644 --- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex +++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.RateLimiter do @@ -7,12 +7,14 @@ defmodule Pleroma.Plugs.RateLimiter do ## Configuration - A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: + A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. + The basic configuration is a tuple where: * The first element: `scale` (Integer). The time scale in milliseconds. * The second element: `limit` (Integer). How many requests to limit in the time scale provided. - It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated. + It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a + list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated. To disable a limiter set its value to `nil`. @@ -64,91 +66,102 @@ defmodule Pleroma.Plugs.RateLimiter do import Pleroma.Web.TranslationHelpers import Plug.Conn + alias Pleroma.Config alias Pleroma.Plugs.RateLimiter.LimiterSupervisor alias Pleroma.User require Logger - def init(opts) do - limiter_name = Keyword.get(opts, :name) - - case Pleroma.Config.get([:rate_limit, limiter_name]) do - nil -> - nil - - config -> - name_root = Keyword.get(opts, :bucket_name, limiter_name) + @doc false + def init(plug_opts) do + plug_opts + end - %{ - name: name_root, - limits: config, - opts: opts - } + def call(conn, plug_opts) do + if disabled?() do + handle_disabled(conn) + else + action_settings = action_settings(plug_opts) + handle(conn, action_settings) end end - # Do not limit if there is no limiter configuration - def call(conn, nil), do: conn + defp handle_disabled(conn) do + if Config.get(:env) == :prod do + Logger.warn("Rate limiter is disabled for localhost/socket") + end + + conn + end - def call(conn, settings) do - case disabled?() do - true -> - if Pleroma.Config.get(:env) == :prod, - do: Logger.warn("Rate limiter is disabled for localhost/socket") + defp handle(conn, nil), do: conn + defp handle(conn, action_settings) do + action_settings + |> incorporate_conn_info(conn) + |> check_rate() + |> case do + {:ok, _count} -> conn - false -> - settings - |> incorporate_conn_info(conn) - |> check_rate() - |> case do - {:ok, _count} -> - conn - - {:error, _count} -> - render_throttled_error(conn) - end + {:error, _count} -> + render_throttled_error(conn) end end def disabled? do localhost_or_socket = - Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip]) + Config.get([Pleroma.Web.Endpoint, :http, :ip]) |> Tuple.to_list() |> Enum.join(".") |> String.match?(~r/^local|^127.0.0.1/) - remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled]) + remote_ip_disabled = not Config.get([Pleroma.Plugs.RemoteIp, :enabled]) localhost_or_socket and remote_ip_disabled end - def inspect_bucket(conn, name_root, settings) do - settings = - settings - |> incorporate_conn_info(conn) + @inspect_bucket_not_found {:error, :not_found} - bucket_name = make_bucket_name(%{settings | name: name_root}) - key_name = make_key_name(settings) - limit = get_limits(settings) + def inspect_bucket(conn, bucket_name_root, plug_opts) do + with %{name: _} = action_settings <- action_settings(plug_opts) do + action_settings = incorporate_conn_info(action_settings, conn) + bucket_name = make_bucket_name(%{action_settings | name: bucket_name_root}) + key_name = make_key_name(action_settings) + limit = get_limits(action_settings) - case Cachex.get(bucket_name, key_name) do - {:error, :no_cache} -> - {:err, :not_found} + case Cachex.get(bucket_name, key_name) do + {:error, :no_cache} -> + @inspect_bucket_not_found - {:ok, nil} -> - {0, limit} + {:ok, nil} -> + {0, limit} - {:ok, value} -> - {value, limit - value} + {:ok, value} -> + {value, limit - value} + end + else + _ -> @inspect_bucket_not_found end end - defp check_rate(settings) do - bucket_name = make_bucket_name(settings) - key_name = make_key_name(settings) - limit = get_limits(settings) + def action_settings(plug_opts) do + with limiter_name when is_atom(limiter_name) <- plug_opts[:name], + limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do + bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name) + + %{ + name: bucket_name_root, + limits: limits, + opts: plug_opts + } + end + end + + defp check_rate(action_settings) do + bucket_name = make_bucket_name(action_settings) + key_name = make_key_name(action_settings) + limit = get_limits(action_settings) case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do {:commit, value} -> @@ -158,8 +171,8 @@ defmodule Pleroma.Plugs.RateLimiter do {:error, value} {:error, :no_cache} -> - initialize_buckets(settings) - check_rate(settings) + initialize_buckets!(action_settings) + check_rate(action_settings) end end @@ -169,16 +182,19 @@ defmodule Pleroma.Plugs.RateLimiter do defp increment_value(val, _limit), do: {:commit, val + 1} - defp incorporate_conn_info(settings, %{assigns: %{user: %User{id: user_id}}, params: params}) do - Map.merge(settings, %{ + defp incorporate_conn_info(action_settings, %{ + assigns: %{user: %User{id: user_id}}, + params: params + }) do + Map.merge(action_settings, %{ mode: :user, conn_params: params, conn_info: "#{user_id}" }) end - defp incorporate_conn_info(settings, %{params: params} = conn) do - Map.merge(settings, %{ + defp incorporate_conn_info(action_settings, %{params: params} = conn) do + Map.merge(action_settings, %{ mode: :anon, conn_params: params, conn_info: "#{ip(conn)}" @@ -197,10 +213,10 @@ defmodule Pleroma.Plugs.RateLimiter do |> halt() end - defp make_key_name(settings) do + defp make_key_name(action_settings) do "" - |> attach_params(settings) - |> attach_identity(settings) + |> attach_selected_params(action_settings) + |> attach_identity(action_settings) end defp get_scale(_, {scale, _}), do: scale @@ -215,28 +231,35 @@ defmodule Pleroma.Plugs.RateLimiter do defp get_limits(%{limits: [{_, limit}, _]}), do: limit - defp make_bucket_name(%{mode: :user, name: name_root}), - do: user_bucket_name(name_root) + defp make_bucket_name(%{mode: :user, name: bucket_name_root}), + do: user_bucket_name(bucket_name_root) - defp make_bucket_name(%{mode: :anon, name: name_root}), - do: anon_bucket_name(name_root) + defp make_bucket_name(%{mode: :anon, name: bucket_name_root}), + do: anon_bucket_name(bucket_name_root) - defp attach_params(input, %{conn_params: conn_params, opts: opts}) do - param_string = - opts + defp attach_selected_params(input, %{conn_params: conn_params, opts: plug_opts}) do + params_string = + plug_opts |> Keyword.get(:params, []) |> Enum.sort() |> Enum.map(&Map.get(conn_params, &1, "")) |> Enum.join(":") - "#{input}#{param_string}" + [input, params_string] + |> Enum.join(":") + |> String.replace_leading(":", "") end - defp initialize_buckets(%{name: _name, limits: nil}), do: :ok + defp initialize_buckets!(%{name: _name, limits: nil}), do: :ok + + defp initialize_buckets!(%{name: name, limits: limits}) do + {:ok, _pid} = + LimiterSupervisor.add_or_return_limiter(anon_bucket_name(name), get_scale(:anon, limits)) + + {:ok, _pid} = + LimiterSupervisor.add_or_return_limiter(user_bucket_name(name), get_scale(:user, limits)) - defp initialize_buckets(%{name: name, limits: limits}) do - LimiterSupervisor.add_limiter(anon_bucket_name(name), get_scale(:anon, limits)) - LimiterSupervisor.add_limiter(user_bucket_name(name), get_scale(:user, limits)) + :ok end defp attach_identity(base, %{mode: :user, conn_info: conn_info}), @@ -245,6 +268,6 @@ defmodule Pleroma.Plugs.RateLimiter do defp attach_identity(base, %{mode: :anon, conn_info: conn_info}), do: "ip:#{base}:#{conn_info}" - defp user_bucket_name(name_root), do: "user:#{name_root}" |> String.to_atom() - defp anon_bucket_name(name_root), do: "anon:#{name_root}" |> String.to_atom() + defp user_bucket_name(bucket_name_root), do: "user:#{bucket_name_root}" |> String.to_atom() + defp anon_bucket_name(bucket_name_root), do: "anon:#{bucket_name_root}" |> String.to_atom() end diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex index 1cd5af48a..2eca4f8f6 100644 --- a/lib/pleroma/plugs/remote_ip.ex +++ b/lib/pleroma/plugs/remote_ip.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.RemoteIp do diff --git a/lib/pleroma/plugs/user_enabled_plug.ex b/lib/pleroma/plugs/user_enabled_plug.ex index 7b304eebc..23e800a74 100644 --- a/lib/pleroma/plugs/user_enabled_plug.ex +++ b/lib/pleroma/plugs/user_enabled_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.UserEnabledPlug do diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex index 3190163d3..2748102df 100644 --- a/lib/pleroma/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/plugs/user_is_admin_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.UserIsAdminPlug do diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index cb0b6653c..f62138466 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo do diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index e81bfcd7d..8ff06a462 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ScheduledActivity do diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index cf590fb01..33f50dda8 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Stats do import Ecto.Query + alias Pleroma.CounterCache alias Pleroma.Repo alias Pleroma.User @@ -96,4 +97,21 @@ defmodule Pleroma.Stats do } } end + + def get_status_visibility_count do + counter_cache = + CounterCache.get_as_map([ + "status_visibility_public", + "status_visibility_private", + "status_visibility_unlisted", + "status_visibility_direct" + ]) + + %{ + public: counter_cache["status_visibility_public"] || 0, + unlisted: counter_cache["status_visibility_unlisted"] || 0, + private: counter_cache["status_visibility_private"] || 0, + direct: counter_cache["status_visibility_direct"] || 0 + } + end end diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 2e0986197..440199d19 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -37,6 +37,7 @@ defmodule Pleroma.Upload do Plug.Upload.t() | (data_uri_string :: String.t()) | {:from_local, name :: String.t(), id :: String.t(), path :: String.t()} + | map() @type option :: {:type, :avatar | :banner | :background} diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index 2e6fe3292..10b3069f4 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Local do diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index feb89cea6..a13ff23b6 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.S3 do diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index d71e213d2..9a94534e9 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Uploader do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 5ea36fea3..5fe79333e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User do @@ -749,9 +749,18 @@ defmodule Pleroma.User do Cachex.del(:user_cache, "nickname:#{user.nickname}") end + @spec get_cached_by_ap_id(String.t()) :: User.t() | nil def get_cached_by_ap_id(ap_id) do key = "ap_id:#{ap_id}" - Cachex.fetch!(:user_cache, key, fn _ -> get_by_ap_id(ap_id) end) + + with {:ok, nil} <- Cachex.get(:user_cache, key), + user when not is_nil(user) <- get_by_ap_id(ap_id), + {:ok, true} <- Cachex.put(:user_cache, key, user) do + user + else + {:ok, user} -> user + nil -> nil + end end def get_cached_by_id(id) do @@ -853,14 +862,14 @@ defmodule Pleroma.User do @spec get_followers_query(User.t()) :: Ecto.Query.t() def get_followers_query(user), do: get_followers_query(user, nil) - @spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())} + @spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())} def get_followers(user, page \\ nil) do user |> get_followers_query(page) |> Repo.all() end - @spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())} + @spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())} def get_external_followers(user, page \\ nil) do user |> get_followers_query(page) @@ -1304,7 +1313,6 @@ defmodule Pleroma.User do Repo.delete(user) end - @spec perform(atom(), User.t()) :: {:ok, User.t()} def perform(:fetch_initial_posts, %User{} = user) do pages = Pleroma.Config.get!([:fetch_initial_posts, :pages]) @@ -1336,7 +1344,6 @@ defmodule Pleroma.User do ) end - @spec perform(atom(), User.t(), list()) :: list() | {:error, any()} def perform(:follow_import, %User{} = follower, followed_identifiers) when is_list(followed_identifiers) do Enum.map( diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 364bc1c89..884e33039 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Query do @@ -48,7 +48,7 @@ defmodule Pleroma.User.Query do followers: User.t(), friends: User.t(), recipients_from_activity: [String.t()], - nickname: [String.t()], + nickname: [String.t()] | String.t(), ap_id: [String.t()], order_by: term(), select: term(), diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index 6b55df483..cec59c372 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Search do @@ -33,9 +33,15 @@ defmodule Pleroma.User.Search do # Strip the beginning @ off if there is a query query_string = String.trim_leading(query_string, "@") - with [name, domain] <- String.split(query_string, "@"), - formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do - name <> "@" <> to_string(:idna.encode(formatted_domain)) + with [name, domain] <- String.split(query_string, "@") do + encoded_domain = + domain + |> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") + |> String.to_charlist() + |> :idna.encode() + |> to_string() + + name <> "@" <> encoded_domain else _ -> query_string end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 3149e10e9..393947942 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserRelationship do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 408f6c966..07121a798 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity.Ir.Topics alias Pleroma.ActivityExpiration alias Pleroma.Config + alias Pleroma.Constants alias Pleroma.Conversation alias Pleroma.Conversation.Participation alias Pleroma.Notification @@ -125,6 +126,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def increase_poll_votes_if_vote(_create_data), do: :noop + @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()} def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map, fake), @@ -242,12 +244,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :noop end - def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do + @spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()} + def create(params, fake \\ false) do + with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do + result + end + end + + defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do additional = params[:additional] || %{} # only accept false as false value local = !(params[:local] == false) published = params[:published] - quick_insert? = Pleroma.Config.get([:env]) == :benchmark + quick_insert? = Config.get([:env]) == :benchmark with create_data <- make_create_data( @@ -270,10 +279,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, activity} {:error, message} -> - {:error, message} + Repo.rollback(message) end end + @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()} def listen(%{to: to, actor: actor, context: context, object: object} = params) do additional = params[:additional] || %{} # only accept false as false value @@ -288,20 +298,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, activity} <- insert(listen_data, local), :ok <- maybe_federate(activity) do {:ok, activity} - else - {:error, message} -> - {:error, message} end end + @spec accept(map()) :: {:ok, Activity.t()} | {:error, any()} def accept(params) do accept_or_reject("Accept", params) end + @spec reject(map()) :: {:ok, Activity.t()} | {:error, any()} def reject(params) do accept_or_reject("Reject", params) end + @spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()} def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do local = Map.get(params, :local, true) activity_id = Map.get(params, :activity_id, nil) @@ -315,6 +325,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + @spec update(map()) :: {:ok, Activity.t()} | {:error, any()} def update(%{to: to, cc: cc, actor: actor, object: object} = params) do local = !(params[:local] == false) activity_id = params[:activity_id] @@ -333,7 +344,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + @spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) :: + {:ok, Activity.t(), Object.t()} | {:error, any()} def react_with_emoji(user, object, emoji, options \\ []) do + with {:ok, result} <- + Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do + result + end + end + + defp do_react_with_emoji(user, object, emoji, options) do with local <- Keyword.get(options, :local, true), activity_id <- Keyword.get(options, :activity_id, nil), true <- Pleroma.Emoji.is_unicode_emoji?(emoji), @@ -343,11 +363,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(activity) do {:ok, activity, object} else - e -> {:error, e} + false -> {:error, false} + {:error, error} -> Repo.rollback(error) end end + @spec unreact_with_emoji(User.t(), String.t(), keyword()) :: + {:ok, Activity.t(), Object.t()} | {:error, any()} def unreact_with_emoji(user, reaction_id, options \\ []) do + with {:ok, result} <- + Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do + result + end + end + + defp do_unreact_with_emoji(user, reaction_id, options) do with local <- Keyword.get(options, :local, true), activity_id <- Keyword.get(options, :activity_id, nil), user_ap_id <- user.ap_id, @@ -359,17 +389,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(activity) do {:ok, activity, object} else - e -> {:error, e} + {:error, error} -> Repo.rollback(error) end end # TODO: This is weird, maybe we shouldn't check here if we can make the activity. - def like( - %User{ap_id: ap_id} = user, - %Object{data: %{"id" => _}} = object, - activity_id \\ nil, - local \\ true - ) do + @spec like(User.t(), Object.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t(), Object.t()} | {:error, any()} + def like(user, object, activity_id \\ nil, local \\ true) do + with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do + result + end + end + + defp do_like( + %User{ap_id: ap_id} = user, + %Object{data: %{"id" => _}} = object, + activity_id, + local + ) do with nil <- get_existing_like(ap_id, object), like_data <- make_like_data(user, object, activity_id), {:ok, activity} <- insert(like_data, local), @@ -377,12 +415,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(activity) do {:ok, activity, object} else - %Activity{} = activity -> {:ok, activity, object} - error -> {:error, error} + %Activity{} = activity -> + {:ok, activity, object} + + {:error, error} -> + Repo.rollback(error) end end + @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()} def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do + with {:ok, result} <- + Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do + result + end + end + + defp do_unlike(actor, object, activity_id, local) do with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object), unlike_data <- make_unlike_data(actor, like_activity, activity_id), {:ok, unlike_activity} <- insert(unlike_data, local), @@ -391,10 +441,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(unlike_activity) do {:ok, unlike_activity, like_activity, object} else - _e -> {:ok, object} + nil -> {:ok, object} + {:error, error} -> Repo.rollback(error) end end + @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) :: + {:ok, Activity.t(), Object.t()} | {:error, any()} def announce( %User{ap_id: _} = user, %Object{data: %{"id" => _}} = object, @@ -402,6 +455,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do local \\ true, public \\ true ) do + with {:ok, result} <- + Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do + result + end + end + + defp do_announce(user, object, activity_id, local, public) do with true <- is_announceable?(object, user, public), announce_data <- make_announce_data(user, object, activity_id, public), {:ok, activity} <- insert(announce_data, local), @@ -409,16 +469,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(activity) do {:ok, activity, object} else - error -> {:error, error} + false -> {:error, false} + {:error, error} -> Repo.rollback(error) end end + @spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()} def unannounce( %User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true ) do + with {:ok, result} <- + Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do + result + end + end + + defp do_unannounce(actor, object, activity_id, local) do with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object), unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id), {:ok, unannounce_activity} <- insert(unannounce_data, local), @@ -427,30 +497,61 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, object} <- remove_announce_from_object(announce_activity, object) do {:ok, unannounce_activity, object} else - _e -> {:ok, object} + nil -> {:ok, object} + {:error, error} -> Repo.rollback(error) end end + @spec follow(User.t(), User.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t()} | {:error, any()} def follow(follower, followed, activity_id \\ nil, local \\ true) do + with {:ok, result} <- + Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do + result + end + end + + defp do_follow(follower, followed, activity_id, local) do with data <- make_follow_data(follower, followed, activity_id), {:ok, activity} <- insert(data, local), :ok <- maybe_federate(activity), _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do {:ok, activity} + else + {:error, error} -> Repo.rollback(error) end end + @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t()} | nil | {:error, any()} def unfollow(follower, followed, activity_id \\ nil, local \\ true) do + with {:ok, result} <- + Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do + result + end + end + + defp do_unfollow(follower, followed, activity_id, local) do with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed), {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"), unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id), {:ok, activity} <- insert(unfollow_data, local), :ok <- maybe_federate(activity) do {:ok, activity} + else + nil -> nil + {:error, error} -> Repo.rollback(error) + end + end + + @spec delete(User.t() | Object.t(), keyword()) :: {:ok, User.t() | Object.t()} | {:error, any()} + def delete(entity, options \\ []) do + with {:ok, result} <- Repo.transaction(fn -> do_delete(entity, options) end) do + result end end - def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do + defp do_delete(%User{ap_id: ap_id, follower_address: follower_address} = user, _) do with data <- %{ "to" => [follower_address], "type" => "Delete", @@ -463,7 +564,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do + defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options) do local = Keyword.get(options, :local, true) activity_id = Keyword.get(options, :activity_id, nil) actor = Keyword.get(options, :actor, actor) @@ -488,11 +589,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:ok, _actor} <- decrease_note_count_if_public(user, object), :ok <- maybe_federate(activity) do {:ok, activity} + else + {:error, error} -> + Repo.rollback(error) end end - @spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil} + @spec block(User.t(), User.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t()} | {:error, any()} def block(blocker, blocked, activity_id \\ nil, local \\ true) do + with {:ok, result} <- + Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do + result + end + end + + defp do_block(blocker, blocked, activity_id, local) do outgoing_blocks = Config.get([:activitypub, :outgoing_blocks]) unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) @@ -507,20 +619,32 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do :ok <- maybe_federate(activity) do {:ok, activity} else - _e -> {:ok, nil} + {:error, error} -> Repo.rollback(error) end end + @spec unblock(User.t(), User.t(), String.t() | nil, boolean()) :: + {:ok, Activity.t()} | {:error, any()} | nil def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do + with {:ok, result} <- + Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do + result + end + end + + defp do_unblock(blocker, blocked, activity_id, local) do with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked), unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id), {:ok, activity} <- insert(unblock_data, local), :ok <- maybe_federate(activity) do {:ok, activity} + else + nil -> nil + {:error, error} -> Repo.rollback(error) end end - @spec flag(map()) :: {:ok, Activity.t()} | any + @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()} def flag( %{ actor: actor, @@ -557,6 +681,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()} def move(%User{} = origin, %User{} = target, local \\ true) do params = %{ "type" => "Move", @@ -582,7 +707,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp fetch_activities_for_context_query(context, opts) do - public = [Pleroma.Constants.as_public()] + public = [Constants.as_public()] recipients = if opts["user"], @@ -627,10 +752,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Repo.one() end + @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do opts = Map.drop(opts, ["user"]) - [Pleroma.Constants.as_public()] + [Constants.as_public()] |> fetch_activities_query(opts) |> restrict_unlisted() |> Pagination.fetch_paginated(opts, pagination) @@ -781,13 +907,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Enum.reverse() end - def fetch_instance_activities(params) do + def fetch_statuses(reading_user, params) do params = params |> Map.put("type", ["Create", "Announce"]) - |> Map.put("instance", params["instance"]) - fetch_activities([Pleroma.Constants.as_public()], params, :offset) + recipients = + user_activities_recipients(%{ + "godmode" => params["godmode"], + "reading_user" => reading_user + }) + + fetch_activities(recipients, params, :offset) |> Enum.reverse() end @@ -797,9 +928,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp user_activities_recipients(%{"reading_user" => reading_user}) do if reading_user do - [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)] + [Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)] else - [Pleroma.Constants.as_public()] + [Constants.as_public()] end end @@ -1006,7 +1137,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do fragment( "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)", activity.data, - ^[Pleroma.Constants.as_public()] + ^[Constants.as_public()] ) ) end @@ -1180,7 +1311,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @doc """ Fetch favorites activities of user with order by sort adds to favorites """ - @spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t()) + @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t()) def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do user.ap_id |> Activity.Queries.by_actor() @@ -1218,7 +1349,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do where: fragment("? && ?", activity.recipients, ^recipients) or (fragment("? && ?", activity.recipients, ^recipients_with_public) and - ^Pleroma.Constants.as_public() in activity.recipients) + ^Constants.as_public() in activity.recipients) ) end @@ -1234,6 +1365,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Enum.reverse() end + @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()} def upload(file, opts \\ []) do with {:ok, data} <- Upload.store(file, opts) do obj_data = @@ -1331,8 +1463,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp normalize_counter(_), do: 0 defp maybe_update_follow_information(data) do - with {:enabled, true} <- - {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])}, + with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])}, {:ok, info} <- fetch_follow_information_for_user(data) do info = Map.merge(data[:info] || %{}, info) Map.put(data, :info, info) diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index 8abe18e29..9e7800997 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do @@ -17,6 +17,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do # does the post contain links? defp contains_links?(%{"content" => content} = _object) do content + |> Floki.parse_fragment!() |> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"],a.zrl") |> Floki.attribute("a", "href") |> length() > 0 diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index df774b0f7..d9a0acfd3 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex index 878c57925..cc2ac9d08 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 8b36c1021..4a8bc91ae 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -1,16 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do alias Pleroma.Config alias Pleroma.User - alias Pleroma.Web.ActivityPub.MRF require Pleroma.Constants @moduledoc "Filter activities depending on their age" - @behaviour MRF + @behaviour Pleroma.Web.ActivityPub.MRF defp check_date(%{"published" => published} = message) do with %DateTime{} = now <- DateTime.utc_now(), diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 8e53296e7..4edc007fd 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -1,12 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF @moduledoc "Filter activities depending on their origin instance" - @behaviour MRF + @behaviour Pleroma.Web.ActivityPub.MRF require Pleroma.Constants diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index 566c1e191..c9f20571f 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do @@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do require Logger - @behaviour MRF + @behaviour Pleroma.Web.ActivityPub.MRF defp lookup_subchain(actor) do with matches <- Config.get([:mrf_subchain, :match_actor]), diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index 7389d6a96..a927a4ed8 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index c184c3b66..6167a74e2 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index e4e3ab44a..6c558e7f0 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Publisher do diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 48a1b71e0..bb5542c89 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Relay do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a72d8430f..9cd3de705 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier do @@ -156,10 +156,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do when not is_nil(in_reply_to) do in_reply_to_id = prepare_in_reply_to(in_reply_to) object = Map.put(object, "inReplyToAtomUri", in_reply_to_id) + depth = (options[:depth] || 0) + 1 - if Federator.allowed_incoming_reply_depth?(options[:depth]) do + if Federator.allowed_thread_distance?(depth) do with {:ok, replied_object} <- get_obj_helper(in_reply_to_id, options), - %Activity{} = _ <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do + %Activity{} <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do object |> Map.put("inReplyTo", replied_object.data["id"]) |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id) @@ -312,7 +313,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options) when is_binary(reply_id) do - with true <- Federator.allowed_incoming_reply_depth?(options[:depth]), + with true <- Federator.allowed_thread_distance?(options[:depth]), {:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do Map.put(object, "type", "Answer") else @@ -406,8 +407,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do with nil <- Activity.get_create_by_object_ap_id(object["id"]), {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do - options = Keyword.put(options, :depth, (options[:depth] || 0) + 1) - object = fix_object(data["object"], options) + object = fix_object(object, options) params = %{ to: data["to"], @@ -424,7 +424,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ]) } - ActivityPub.create(params) + with {:ok, created_activity} <- ActivityPub.create(params) do + reply_depth = (options[:depth] || 0) + 1 + + if Federator.allowed_thread_distance?(reply_depth) do + for reply_id <- replies(object) do + Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{ + "id" => reply_id, + "depth" => reply_depth + }) + end + end + + {:ok, created_activity} + end else %Activity{} = activity -> {:ok, activity} _e -> :error @@ -442,7 +455,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_addressing with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do - options = Keyword.put(options, :depth, (options[:depth] || 0) + 1) + reply_depth = (options[:depth] || 0) + 1 + options = Keyword.put(options, :depth, reply_depth) object = fix_object(object, options) params = %{ @@ -903,6 +917,50 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def set_reply_to_uri(obj), do: obj + @doc """ + Serialized Mastodon-compatible `replies` collection containing _self-replies_. + Based on Mastodon's ActivityPub::NoteSerializer#replies. + """ + def set_replies(obj_data) do + replies_uris = + with limit when limit > 0 <- + Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0), + %Object{} = object <- Object.get_cached_by_ap_id(obj_data["id"]) do + object + |> Object.self_replies() + |> select([o], fragment("?->>'id'", o.data)) + |> limit(^limit) + |> Repo.all() + else + _ -> [] + end + + set_replies(obj_data, replies_uris) + end + + defp set_replies(obj, []) do + obj + end + + defp set_replies(obj, replies_uris) do + replies_collection = %{ + "type" => "Collection", + "items" => replies_uris + } + + Map.merge(obj, %{"replies" => replies_collection}) + end + + def replies(%{"replies" => %{"first" => %{"items" => items}}}) when not is_nil(items) do + items + end + + def replies(%{"replies" => %{"items" => items}}) when not is_nil(items) do + items + end + + def replies(_), do: [] + # Prepares the object of an outgoing create activity. def prepare_object(object) do object @@ -914,6 +972,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> prepare_attachments |> set_conversation |> set_reply_to_uri + |> set_replies |> strip_internal_fields |> strip_internal_tags |> set_type diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 10ce5eee8..2bc958670 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Utils do @@ -45,8 +45,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do Map.put(params, "actor", get_ap_id(params["actor"])) end - @spec determine_explicit_mentions(map()) :: map() - def determine_explicit_mentions(%{"tag" => tag} = _) when is_list(tag) do + @spec determine_explicit_mentions(map()) :: [any] + def determine_explicit_mentions(%{"tag" => tag}) when is_list(tag) do Enum.flat_map(tag, fn %{"type" => "Mention", "href" => href} -> [href] _ -> [] @@ -427,7 +427,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do @doc """ Updates a follow activity's state (for locked accounts). """ - @spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity} | {:error, any()} + @spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity | nil} def update_follow_state_for_all( %Activity{data: %{"actor" => actor, "object" => object}} = activity, state diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index c95cd182d..558832703 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIController do @@ -8,10 +8,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do import Pleroma.Web.ControllerHelper, only: [json_response: 3] alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.ConfigDB alias Pleroma.ModerationLog alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.ReportNote + alias Pleroma.Stats alias Pleroma.User alias Pleroma.UserInviteToken alias Pleroma.Web.ActivityPub.ActivityPub @@ -97,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["read"], admin: true} - when action in [:config_show, :list_log] + when action in [:config_show, :list_log, :stats] ) plug( @@ -242,13 +244,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def list_instance_statuses(conn, %{"instance" => instance} = params) do + with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true {page, page_size} = page_params(params) activities = - ActivityPub.fetch_instance_activities(%{ + ActivityPub.fetch_statuses(nil, %{ "instance" => instance, "limit" => page_size, - "offset" => (page - 1) * page_size + "offset" => (page - 1) * page_size, + "exclude_reblogs" => !with_reblogs && "true" }) conn @@ -257,6 +261,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def list_user_statuses(conn, %{"nickname" => nickname} = params) do + with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true godmode = params["godmode"] == "true" || params["godmode"] == true with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do @@ -265,7 +270,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do activities = ActivityPub.fetch_user_activities(user, nil, %{ "limit" => page_size, - "godmode" => godmode + "godmode" => godmode, + "exclude_reblogs" => !with_reblogs && "true" }) conn @@ -570,8 +576,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do @doc "Sends registration invite via email" def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do with true <- - Pleroma.Config.get([:instance, :invites_enabled]) && - !Pleroma.Config.get([:instance, :registrations_open]), + Config.get([:instance, :invites_enabled]) && + !Config.get([:instance, :registrations_open]), {:ok, invite_token} <- UserInviteToken.create_invite(), email <- Pleroma.Emails.UserEmail.user_invitation_email( @@ -739,6 +745,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def list_statuses(%{assigns: %{user: admin}} = conn, params) do + godmode = params["godmode"] == "true" || params["godmode"] == true + local_only = params["local_only"] == "true" || params["local_only"] == true + {page, page_size} = page_params(params) + + activities = + ActivityPub.fetch_statuses(admin, %{ + "godmode" => godmode, + "local_only" => local_only, + "limit" => page_size, + "offset" => (page - 1) * page_size + }) + + conn + |> put_view(Pleroma.Web.AdminAPI.StatusView) + |> render("index.json", %{activities: activities, as: :activity}) + end + def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"]) @@ -808,7 +832,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do configs = ConfigDB.get_all_as_keyword() merged = - Pleroma.Config.Holder.config() + Config.Holder.config() |> ConfigDB.merge(configs) |> Enum.map(fn {group, values} -> Enum.map(values, fn {key, value} -> @@ -838,7 +862,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end) |> List.flatten() - json(conn, %{configs: merged}) + response = %{configs: merged} + + response = + if Restarter.Pleroma.need_reboot?() do + Map.put(response, :need_reboot, true) + else + response + end + + json(conn, response) end end @@ -863,20 +896,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do Ecto.get_meta(config, :state) == :deleted end) - Pleroma.Config.TransferTask.load_and_update_env(deleted, false) + Config.TransferTask.load_and_update_env(deleted, false) need_reboot? = - Enum.any?(updated, fn config -> - group = ConfigDB.from_string(config.group) - key = ConfigDB.from_string(config.key) - value = ConfigDB.from_binary(config.value) - Pleroma.Config.TransferTask.pleroma_need_restart?(group, key, value) - end) + Restarter.Pleroma.need_reboot?() || + Enum.any?(updated, fn config -> + group = ConfigDB.from_string(config.group) + key = ConfigDB.from_string(config.key) + value = ConfigDB.from_binary(config.value) + Config.TransferTask.pleroma_need_restart?(group, key, value) + end) response = %{configs: updated} response = - if need_reboot?, do: Map.put(response, :need_reboot, need_reboot?), else: response + if need_reboot? do + Restarter.Pleroma.need_reboot() + Map.put(response, :need_reboot, need_reboot?) + else + response + end conn |> put_view(ConfigView) @@ -886,18 +925,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def restart(conn, _params) do with :ok <- configurable_from_database(conn) do - if Pleroma.Config.get(:env) == :test do - Logger.warn("pleroma restarted") - else - send(Restarter.Pleroma, {:restart, 50}) - end + Restarter.Pleroma.restart(Config.get(:env), 50) json(conn, %{}) end end defp configurable_from_database(conn) do - if Pleroma.Config.get(:configurable_from_database) do + if Config.get(:configurable_from_database) do :ok else errors( @@ -941,6 +976,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do conn |> json("") end + def stats(conn, _) do + count = Stats.get_status_visibility_count() + + conn + |> json(%{"status_visibility" => count}) + end + def errors(conn, {:error, :not_found}) do conn |> put_status(:not_found) diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex index ed919833e..29cea1f44 100644 --- a/lib/pleroma/web/admin_api/search.ex +++ b/lib/pleroma/web/admin_api/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.Search do @@ -18,7 +18,11 @@ defmodule Pleroma.Web.AdminAPI.Search do @spec user(map()) :: {:ok, [User.t()], pos_integer()} def user(params \\ %{}) do - query = User.Query.build(params) |> order_by([u], u.nickname) + query = + params + |> Map.drop([:page, :page_size]) + |> User.Query.build() + |> order_by([u], u.nickname) paginated_query = User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size) diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex index bbb53efcd..587ef760e 100644 --- a/lib/pleroma/web/admin_api/views/config_view.ex +++ b/lib/pleroma/web/admin_api/views/config_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigView do diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 6f2b2b09c..360ddc22c 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusView do @@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do alias Pleroma.User def render("index.json", opts) do - render_many(opts.activities, __MODULE__, "show.json", opts) + safe_render_many(opts.activities, __MODULE__, "show.json", opts) end def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 03921de27..4ec13aafa 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI do diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index ca6c93862..8746273c4 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.Utils do @@ -228,9 +228,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do data, visibility ) do - no_attachment_links = + attachment_links = data - |> Map.get("no_attachment_links", Config.get([:instance, :no_attachment_links])) + |> Map.get("attachment_links", Config.get([:instance, :attachment_links])) |> truthy_param?() content_type = get_content_type(data["content_type"]) @@ -244,7 +244,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do status |> format_input(content_type, options) - |> maybe_add_attachments(attachments, no_attachment_links) + |> maybe_add_attachments(attachments, attachment_links) |> maybe_add_nsfw_tag(data) end @@ -270,7 +270,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do def make_context(%Activity{data: %{"context" => context}}, _), do: context def make_context(_, _), do: Utils.generate_context_id() - def maybe_add_attachments(parsed, _attachments, true = _no_links), do: parsed + def maybe_add_attachments(parsed, _attachments, false = _no_links), do: parsed def maybe_add_attachments({text, mentions, tags}, attachments, _no_links) do text = add_attachments(text, attachments) diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index e3d7a465b..c9a3a2585 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ControllerHelper do diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index a77b73109..118c3ac6f 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Endpoint do diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index f506a7d24..fd904ef0a 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator do @@ -15,13 +15,19 @@ defmodule Pleroma.Web.Federator do require Logger - @doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)" + @doc """ + Returns `true` if the distance to target object does not exceed max configured value. + Serves to prevent fetching of very long threads, especially useful on smaller instances. + Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161). + Applies to fetching of both ancestor (reply-to) and child (reply) objects. + """ # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength - def allowed_incoming_reply_depth?(depth) do - max_replies_depth = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth]) + def allowed_thread_distance?(distance) do + max_distance = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth]) - if max_replies_depth do - (depth || 1) <= max_replies_depth + if max_distance && max_distance >= 0 do + # Default depth is 0 (an object has zero distance from itself in its thread) + (distance || 0) <= max_distance else true end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index 334802e0a..e18adaea8 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.FeedView do diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index 9accd0872..75c9ea17e 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.TagController do diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index f5096834b..59aabb549 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.UserController do diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 9f7e4943c..43649ad26 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastoFEController do diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index f2508aca4..0c9218454 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationController do diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 5a5db8e00..fcab4ef63 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SearchController do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 287d1631c..b0048102f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusController do diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 11f7b85d3..11df6fc4a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionController do diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index b9cc8f104..0cdc7bd8d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SuggestionController do diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 29964a1d4..09e08271b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineController do @@ -10,9 +10,20 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.Pagination alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + # TODO: Replace with a macro when there is a Phoenix release with + # https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e + # in it + + plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct) + plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public) + plug(RateLimiter, [name: :timeline, bucket_name: :home_timeline] when action == :home) + plug(RateLimiter, [name: :timeline, bucket_name: :hashtag_timeline] when action == :hashtag) + plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list) + plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 390a2b190..3fe2be521 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPI do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index c6d37ead7..6dc191250 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountView do diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index beba89edb..d934e2107 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppView do diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 360ec10f0..33145c484 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationView do diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 6bb3652fb..40edbb213 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollView do diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e1e92034f..f7469cdff 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusView do @@ -175,9 +175,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do expires_at = with true <- client_posted_this_activity, - expiration when not is_nil(expiration) <- + %ActivityExpiration{scheduled_at: scheduled_at} <- ActivityExpiration.get_by_activity_id(activity.id) do - expiration.scheduled_at + scheduled_at + else + _ -> nil end thread_muted? = @@ -321,11 +323,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do nil end - site_name = rich_media[:site_name] || page_url_data.host - %{ type: "link", - provider_name: site_name, + provider_name: page_url_data.host, provider_url: page_url_data.scheme <> "://" <> page_url_data.host, url: page_url, image: image_url |> MediaProxy.url(), diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex index ee48913a7..bd1459a17 100644 --- a/lib/pleroma/web/metadata/feed.ex +++ b/lib/pleroma/web/metadata/feed.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.Feed do diff --git a/lib/pleroma/web/metadata/rel_me.ex b/lib/pleroma/web/metadata/rel_me.ex index f87fc1973..8905c9c72 100644 --- a/lib/pleroma/web/metadata/rel_me.ex +++ b/lib/pleroma/web/metadata/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RelMe do @@ -8,8 +8,10 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do @impl Provider def build_tags(%{user: user}) do - (Floki.attribute(user.bio, "link[rel~=me]", "href") ++ - Floki.attribute(user.bio, "a[rel~=me]", "href")) + bio_tree = Floki.parse_fragment!(user.bio) + + (Floki.attribute(bio_tree, "link[rel~=me]", "href") ++ + Floki.attribute(bio_tree, "a[rel~=me]", "href")) |> Enum.map(fn link -> {:link, [rel: "me", href: link], []} end) diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 000bd9f66..2f0dfb474 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Utils do diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 03c35cc2a..18eb41333 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Nodeinfo.NodeinfoController do @@ -46,10 +46,10 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do data |> Map.merge(%{quarantined_instances: quarantined}) - |> Map.put(:enabled, Config.get([:instance, :federating])) else %{} end + |> Map.put(:enabled, Config.get([:instance, :federating])) features = [ @@ -92,9 +92,9 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do openRegistrations: Config.get([:instance, :registrations_open]), usage: %{ users: %{ - total: stats.user_count || 0 + total: Map.get(stats, :user_count, 0) }, - localPosts: stats.status_count || 0 + localPosts: Map.get(stats, :status_count, 0) }, metadata: %{ nodeName: Config.get([:instance, :name]), diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 528f08574..46688db7e 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthController do diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex index 151467494..8ecf901f3 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Scopes do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index a2f6d2287..03e95e020 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -323,7 +323,7 @@ keeping it in cache for #{div(cache_ms, 1000)}s") {:ok, _} -> conn |> json("ok") - {:error, _} -> + {:error, _, _} -> conn |> put_status(:internal_server_error) |> json(%{error: "Couldn't delete the pack #{name}"}) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 108e48438..0e160bbfc 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do @@ -41,24 +41,29 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do + def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <- Object.normalize(activity) do reactions = emoji_reactions |> Enum.map(fn [emoji, user_ap_ids] -> - users = - Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) - |> Enum.filter(& &1) - - %{ - name: emoji, - count: length(users), - accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), - me: !!(user && user.ap_id in user_ap_ids) - } + if params["emoji"] && params["emoji"] != emoji do + nil + else + users = + Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) + |> Enum.filter(& &1) + + %{ + name: emoji, + count: length(users), + accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), + me: !!(user && user.ap_id in user_ap_ids) + } + end end) + |> Enum.filter(& &1) conn |> json(reactions) diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex index 16b1a53d2..e97c398dc 100644 --- a/lib/pleroma/web/rel_me.ex +++ b/lib/pleroma/web/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RelMe do @@ -27,9 +27,10 @@ defmodule Pleroma.Web.RelMe do defp parse_url(url) do with {:ok, %Tesla.Env{body: html, status: status}} when status in 200..299 <- Pleroma.HTTP.get(url, [], adapter: @hackney_options), + {:ok, html_tree} <- Floki.parse_document(html), data <- - Floki.attribute(html, "link[rel~=me]", "href") ++ - Floki.attribute(html, "a[rel~=me]", "href") do + Floki.attribute(html_tree, "link[rel~=me]", "href") ++ + Floki.attribute(html_tree, "a[rel~=me]", "href") do {:ok, data} end rescue diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index c06b0a0f2..0779065ee 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser do @@ -81,18 +81,18 @@ defmodule Pleroma.Web.RichMedia.Parser do {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options) html - |> parse_html + |> parse_html() |> maybe_parse() |> Map.put(:url, url) |> clean_parsed_data() |> check_parsed_data() rescue e -> - {:error, "Parsing error: #{inspect(e)}"} + {:error, "Parsing error: #{inspect(e)} #{inspect(__STACKTRACE__)}"} end end - defp parse_html(html), do: Floki.parse(html) + defp parse_html(html), do: Floki.parse_document!(html) defp maybe_parse(html) do Enum.reduce_while(parsers(), %{}, fn parser, acc -> diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index fae3c462e..ae0f36702 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 897215698..980242c68 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Router do @@ -192,6 +192,7 @@ defmodule Pleroma.Web.Router do put("/statuses/:id", AdminAPIController, :status_update) delete("/statuses/:id", AdminAPIController, :status_delete) + get("/statuses", AdminAPIController, :list_statuses) get("/config", AdminAPIController, :config_show) post("/config", AdminAPIController, :config_update) @@ -201,6 +202,7 @@ defmodule Pleroma.Web.Router do get("/moderation_log", AdminAPIController, :list_log) post("/reload_emoji", AdminAPIController, :reload_emoji) + get("/stats", AdminAPIController, :stats) end scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do @@ -271,6 +273,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) + get("/statuses/:id/reactions/:emoji", PleromaAPIController, :emoji_reactions_by) get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by) end diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index 5392c1ec3..29f992a67 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Streamer.Worker do diff --git a/lib/pleroma/web/templates/email/new_users_digest.html.eex b/lib/pleroma/web/templates/email/new_users_digest.html.eex new file mode 100644 index 000000000..40d9b8381 --- /dev/null +++ b/lib/pleroma/web/templates/email/new_users_digest.html.eex @@ -0,0 +1,158 @@ +<%= for {user, total_statuses, latest_status} <- @users_and_statuses do %> + <%# user card START %> + <div style="background-color:transparent;"> + <div class="block-grid mixed-two-up no-stack" + style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> + <div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> + <!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 20px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num3" + style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 20px;"> + <!--<![endif]--> + <div align="left" class="img-container left " + style="padding-right: 0px;padding-left: 0px;"> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img + alt="<%= user.name %>" border="0" class="left " src="<%= avatar_url(user) %>" + style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;" + title="<%= user.name %>" width="76" /> + <!--[if mso]></td></tr></table><![endif]--> + </div> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num9" + style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"> + <!--<![endif]--> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]--> + <div + style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> + <div + style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;"> + <p style="font-size: 14px; line-height: 19px; margin: 0;"><span + style="font-size: 16px; color: <%= @styling.text_color %>;"><%= user.name %></span></p> + <p style="font-size: 14px; line-height: 19px; margin: 0;"><span + style="font-size: 16px;"><%= link "@" <> user.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: admin_user_url(user) %></span></p> + <p style="font-size: 14px; line-height: 19px; margin: 0;"><span + style="font-size: 16px;">Total: <%= total_statuses %></span></p> + </div> + </div> + <!--[if mso]></td></tr></table><![endif]--> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> + </div> + </div> + </div> + <%# user card END %> + + <%= if latest_status do %> + <div style="background-color:transparent;"> + <div class="block-grid" + style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> + <div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> + <!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num12" + style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;"> + <!--<![endif]--> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]--> + <div + style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> + <div + style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;"> + <span style="font-size: 16px; line-height: 19px;"><%= raw latest_status.object.data["content"] %></span></div> + </div> + <!--[if mso]></td></tr></table><![endif]--> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 15px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]--> + <div + style="color:<%= @styling.text_muted_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:15px;"> + <div + style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_muted_color %>;"> + <p style="font-size: 14px; line-height: 16px; margin: 0;"><%= format_date latest_status.object.data["published"] %></p> + </div> + </div> + <!--[if mso]></td></tr></table><![endif]--> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> + </div> + </div> + </div> + <% end %> + <%# divider start %> + <div style="background-color:transparent;"> + <div class="block-grid" + style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> + <div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> + <!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num12" + style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"> + <!--<![endif]--> + <table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation" + style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;" + valign="top" width="100%"> + <tbody> + <tr style="vertical-align: top;" valign="top"> + <td class="divider_inner" + style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;" + valign="top"> + <table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content" + height="0" role="presentation" + style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;" + valign="top" width="100%"> + <tbody> + <tr style="vertical-align: top;" valign="top"> + <td height="0" + style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;" + valign="top"><span></span></td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> + </div> + </div> + </div> + + <%# divider end %> + <%# user card END %> +<% end %> diff --git a/lib/pleroma/web/templates/layout/email_styled.html.eex b/lib/pleroma/web/templates/layout/email_styled.html.eex new file mode 100644 index 000000000..ca2caaf4d --- /dev/null +++ b/lib/pleroma/web/templates/layout/email_styled.html.eex @@ -0,0 +1,193 @@ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office" + xmlns:v="urn:schemas-microsoft-com:vml"> + +<head> + <!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--> + <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> + <meta content="width=device-width" name="viewport" /> + <!--[if !mso]><!--> + <meta content="IE=edge" http-equiv="X-UA-Compatible" /> + <!--<![endif]--> + <title><%= @email.subject %></title> + <!--[if !mso]><!--> + <!--<![endif]--> + <style type="text/css"> + body { + margin: 0; + padding: 0; + } + + a { + + color: <%= @styling.link_color %>; + text-decoration: none; + } + + table, + td, + tr { + vertical-align: top; + border-collapse: collapse; + } + + * { + line-height: inherit; + } + + a[x-apple-data-detectors=true] { + color: inherit !important; + text-decoration: none !important; + } + </style> + <style id="media-query" type="text/css"> + @media (max-width: 610px) { + + .block-grid, + .col { + min-width: 320px !important; + max-width: 100% !important; + display: block !important; + } + + .block-grid { + width: 100% !important; + } + + .col { + width: 100% !important; + } + + .col>div { + margin: 0 auto; + } + + .no-stack .col { + min-width: 0 !important; + display: table-cell !important; + } + + .no-stack.two-up .col { + width: 50% !important; + } + + .no-stack .col.num4 { + width: 33% !important; + } + + .no-stack .col.num8 { + width: 66% !important; + } + + .no-stack .col.num4 { + width: 33% !important; + } + + .no-stack .col.num3 { + width: 25% !important; + } + + .no-stack .col.num6 { + width: 50% !important; + } + + .no-stack .col.num9 { + width: 75% !important; + } + + } + </style> +</head> + +<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: <%= @styling.background_color %>;"> + <!--[if IE]><div class="ie-browser"><![endif]--> + <table bgcolor="<%= @styling.background_color %>" cellpadding="0" cellspacing="0" class="nl-container" role="presentation" + style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: <%= @styling.background_color %>; width: 100%;" + valign="top" width="100%"> + <tbody> + <tr style="vertical-align: top;" valign="top"> + <td style="word-break: break-word; vertical-align: top;" valign="top"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:<%= @styling.background_color %>"><![endif]--> + + <%# header %> + <div style="background-color:transparent;"> + <div class="block-grid" + style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> + <div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> + <!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num12" + style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"> + <!--<![endif]--> + <div align="center" class="img-container center" + style="padding-right: 0px;padding-left: 0px;"> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img + align="center" alt="Image" border="0" class="center" src="<%= @logo_url %>" + style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;" + title="Image" height="80" /> + <!--[if mso]></td></tr></table><![endif]--> + </div> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> + </div> + </div> + </div> + + + <%# title %> + <%= if @title do %> + <div style="background-color:transparent;"> + <div class="block-grid" + style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;"> + <div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;"> + <!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]--> + <!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]--> + <div class="col num12" + style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;"> + <div style="width:100% !important;"> + <!--[if (!mso)&(!IE)]><!--> + <div + style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"> + <!--<![endif]--> + <!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]--> + <div + style="line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;"> + <div + style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;"> + <p style="line-height: 36px; text-align: center; margin: 0;"><span + style="font-size: 30px; color: <%= @styling.header_color %>;"><%= @title %></span></p> + </div> + </div> + <!--[if mso]></td></tr></table><![endif]--> + <!--[if (!mso)&(!IE)]><!--> + </div> + <!--<![endif]--> + </div> + </div> + <!--[if (mso)|(IE)]></td></tr></table><![endif]--> + <!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--> + </div> + </div> + </div> + <% end %> + <%= render @view_module, @view_template, assigns %> + + </td> + </tr> + </tbody> + </table> + <!--[if (IE)]></div><![endif]--> +</body> + +</html> diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index e0d4d5632..fbf31c7eb 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do @@ -69,7 +69,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, {:ok, _, _, _} <- CommonAPI.follow(user, followee) do - render(conn, "followed.html", %{error: false}) + redirect(conn, to: "/users/#{followee.id}") else error -> handle_follow_error(conn, error) @@ -80,7 +80,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, {_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee}, {:ok, _, _, _} <- CommonAPI.follow(user, followee) do - render(conn, "followed.html", %{error: false}) + redirect(conn, to: "/users/#{followee.id}") else error -> handle_follow_error(conn, error) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index f08b9d28c..bca0e26eb 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilController do diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex index d469c4726..c05c7821c 100644 --- a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex +++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index b506a234b..6b0fbe61e 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -12,4 +12,8 @@ defmodule Pleroma.Web.EmailView do |> Timex.parse!("{ISO:Extended:Z}") |> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}") end + + def admin_user_url(%{id: id}) do + Pleroma.Web.Endpoint.url() <> "/pleroma/admin/#/users/" <> id + end end diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index 2cbc6b64d..3c5820a86 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.AttachmentsCleanupWorker do diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index ac2fe6946..598df6580 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackgroundWorker do diff --git a/lib/pleroma/workers/cron/clear_oauth_token_worker.ex b/lib/pleroma/workers/cron/clear_oauth_token_worker.ex index a24407874..341eff054 100644 --- a/lib/pleroma/workers/cron/clear_oauth_token_worker.ex +++ b/lib/pleroma/workers/cron/clear_oauth_token_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.ClearOauthTokenWorker do diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index 0a00129df..c589a59eb 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.DigestEmailsWorker do diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex new file mode 100644 index 000000000..951c2c054 --- /dev/null +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.User + + import Ecto.Query + + use Pleroma.Workers.WorkerHelper, queue: "new_users_digest" + + @impl Oban.Worker + def perform(_args, _job) do + if Pleroma.Config.get([Pleroma.Emails.NewUsersDigestEmail, :enabled]) do + today = NaiveDateTime.utc_now() |> Timex.beginning_of_day() + + a_day_ago = + today + |> Timex.shift(days: -1) + |> Timex.beginning_of_day() + + users_and_statuses = + %{ + local: true, + order_by: :inserted_at + } + |> User.Query.build() + |> where([u], u.inserted_at >= ^a_day_ago and u.inserted_at < ^today) + |> Repo.all() + |> Enum.map(fn user -> + latest_status = + Activity + |> Activity.Queries.by_actor(user.ap_id) + |> Activity.Queries.by_type("Create") + |> Activity.with_preloaded_object() + |> order_by(desc: :inserted_at) + |> limit(1) + |> Repo.one() + + total_statuses = + Activity + |> Activity.Queries.by_actor(user.ap_id) + |> Activity.Queries.by_type("Create") + |> Repo.aggregate(:count, :id) + + {user, total_statuses, latest_status} + end) + + if users_and_statuses != [] do + %{is_admin: true} + |> User.Query.build() + |> Repo.all() + |> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses)) + |> Enum.each(&Pleroma.Emails.Mailer.deliver/1) + end + end + end +end diff --git a/lib/pleroma/workers/cron/purge_expired_activities_worker.ex b/lib/pleroma/workers/cron/purge_expired_activities_worker.ex index 7a52860a9..b8953dd7f 100644 --- a/lib/pleroma/workers/cron/purge_expired_activities_worker.ex +++ b/lib/pleroma/workers/cron/purge_expired_activities_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do diff --git a/lib/pleroma/workers/cron/stats_worker.ex b/lib/pleroma/workers/cron/stats_worker.ex index 425ad41ca..e9b8d59c4 100644 --- a/lib/pleroma/workers/cron/stats_worker.ex +++ b/lib/pleroma/workers/cron/stats_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.StatsWorker do diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex new file mode 100644 index 000000000..ec6534f21 --- /dev/null +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.RemoteFetcherWorker do + alias Pleroma.Object.Fetcher + + use Pleroma.Workers.WorkerHelper, queue: "remote_fetcher" + + @impl Oban.Worker + def perform( + %{ + "op" => "fetch_remote", + "id" => id + } = args, + _job + ) do + {:ok, _object} = Fetcher.fetch_object_from_id(id, depth: args["depth"]) + end +end diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index bd41ab4ce..8905f4ad0 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ScheduledActivityWorker do |