diff options
64 files changed, 853 insertions, 399 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f5d8f612..56b235f6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,6 @@ All notable changes to this project will be documented in this file.  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  ## [unreleased] -### Changed -- **Breaking:** BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines -  ### Removed  - **Breaking:** removed `with_move` parameter from notifications timeline. @@ -21,9 +18,58 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  - Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.  </details> +### Fixed +- Support pagination in conversations API + +## [unreleased-patch] +### Fixed +- Logger configuration through AdminFE + +## [2.0.2] - 2020-04-08 +### Added +- Support for Funkwhale's `Audio` activity +- Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials` + +### Fixed +- Blocked/muted users still generating push notifications +- Input textbox for bio ignoring newlines +- OTP: Inability to use PostgreSQL databases with SSL +- `user delete_activities` breaking when trying to delete already deleted posts +- Incorrect URL for Funkwhale channels + +### Upgrade notes +1. Restart Pleroma + +## [2.0.1] - 2020-03-15 +### Security +- Static-FE: Fix remote posts not being sanitized + +### Fixed +- 500 errors when no `Accept` header is present if Static-FE is enabled +- Instance panel not being updated immediately due to wrong `Cache-Control` headers +- Statuses posted with BBCode/Markdown having unncessary newlines in Pleroma-FE +- OTP: Fix some settings not being migrated to in-database config properly +- No `Cache-Control` headers on attachment/media proxy requests +- Character limit enforcement being off by 1 +- Mastodon Streaming API: hashtag timelines not working + +### Changed +- BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines +- Mastodon API: Allow registration without email if email verification is not enabled + +### Upgrade notes +#### Nginx only +1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header  Cache-Control;` from your config. + +#### Everyone +1. Run database migrations (inside Pleroma directory): +  - OTP: `./bin/pleroma_ctl migrate` +  - From Source: `mix ecto.migrate` +2. Restart Pleroma +  ## [2.0.0] - 2019-03-08  ### Security -- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request. +- Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request.  ### Removed  - **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media` diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex index bd65ac84f..786929ace 100644 --- a/benchmarks/load_testing/fetcher.ex +++ b/benchmarks/load_testing/fetcher.ex @@ -386,47 +386,56 @@ defmodule Pleroma.LoadTesting.Fetcher do      favourites = ActivityPub.fetch_favourites(user) +    output_relationships = +      !!Pleroma.Config.get([:extensions, :output_relationships_in_statuses_by_default]) +      Benchee.run(        %{          "Rendering home timeline" => fn ->            StatusView.render("index.json", %{              activities: home_activities,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: !output_relationships            })          end,          "Rendering direct timeline" => fn ->            StatusView.render("index.json", %{              activities: direct_activities,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: !output_relationships            })          end,          "Rendering public timeline" => fn ->            StatusView.render("index.json", %{              activities: public_activities,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: !output_relationships            })          end,          "Rendering tag timeline" => fn ->            StatusView.render("index.json", %{              activities: tag_activities,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: !output_relationships            })          end,          "Rendering notifications" => fn ->            Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{              notifications: notifications, -            for: user +            for: user, +            skip_relationships: !output_relationships            })          end,          "Rendering favourites timeline" => fn ->            StatusView.render("index.json", %{              activities: favourites,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: !output_relationships            })          end        }, diff --git a/config/config.exs b/config/config.exs index 232a91bf1..7f013aaad 100644 --- a/config/config.exs +++ b/config/config.exs @@ -240,6 +240,8 @@ config :pleroma, :instance,    extended_nickname_format: true,    cleanup_attachments: false +config :pleroma, :extensions, output_relationships_in_statuses_by_default: true +  config :pleroma, :feed,    post_title: %{      max_length: 100, diff --git a/docs/configuration/mrf.md b/docs/configuration/mrf.md index c3957c255..287416b2a 100644 --- a/docs/configuration/mrf.md +++ b/docs/configuration/mrf.md @@ -113,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do    @impl true    def describe do -    {:ok, %{mrf_sample: %{content: "new message content"}}}` +    {:ok, %{mrf_sample: %{content: "new message content"}}}    end  end  ``` diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 4dfcc32e7..3ad6edbfb 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -5,7 +5,6 @@  defmodule Mix.Pleroma do    @doc "Common functions to be reused in mix tasks"    def start_pleroma do -    Mix.Task.run("app.start")      Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)      if Pleroma.Config.get(:env) != :test do diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index dd2b9c8f2..6ab7fe8ef 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -67,7 +67,8 @@ defmodule Mix.Tasks.Pleroma.Benchmark do            Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{              activities: activities,              for: user, -            as: :activity +            as: :activity, +            skip_relationships: true            })          end        }, diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 936bc9ab1..3871e1cbb 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -54,10 +54,19 @@ defmodule Pleroma.Config.TransferTask do            [:pleroma, nil, :prometheus]          end +      {logger, other} = +        (Repo.all(ConfigDB) ++ deleted_settings) +        |> Enum.map(&transform_and_merge/1) +        |> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end) + +      logger +      |> Enum.sort() +      |> Enum.each(&configure/1) +        started_applications = Application.started_applications() -      (Repo.all(ConfigDB) ++ deleted_settings) -      |> Enum.map(&merge_and_update/1) +      other +      |> Enum.map(&update/1)        |> Enum.uniq()        |> Enum.reject(&(&1 in reject_restart))        |> maybe_set_pleroma_last() @@ -81,51 +90,66 @@ defmodule Pleroma.Config.TransferTask do      end    end -  defp group_for_restart(:logger, key, _, merged_value) do -    # change logger configuration in runtime, without restart -    if Keyword.keyword?(merged_value) and -         key not in [:compile_time_application, :backends, :compile_time_purge_matching] do -      Logger.configure_backend(key, merged_value) -    else -      Logger.configure([{key, merged_value}]) -    end +  defp transform_and_merge(%{group: group, key: key, value: value} = setting) do +    group = ConfigDB.from_string(group) +    key = ConfigDB.from_string(key) +    value = ConfigDB.from_binary(value) -    nil -  end +    default = Config.Holder.default_config(group, key) -  defp group_for_restart(group, _, _, _) when group != :pleroma, do: group +    merged = +      cond do +        Ecto.get_meta(setting, :state) == :deleted -> default +        can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value) +        true -> value +      end -  defp group_for_restart(group, key, value, _) do -    if pleroma_need_restart?(group, key, value), do: group +    {group, key, value, merged}    end -  defp merge_and_update(setting) do -    try do -      key = ConfigDB.from_string(setting.key) -      group = ConfigDB.from_string(setting.group) +  # change logger configuration in runtime, without restart +  defp configure({:quack, key, _, merged}) do +    Logger.configure_backend(Quack.Logger, [{key, merged}]) +    :ok = update_env(:quack, key, merged) +  end -      default = Config.Holder.default_config(group, key) -      value = ConfigDB.from_binary(setting.value) +  defp configure({_, :backends, _, merged}) do +    # removing current backends +    Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1) -      merged_value = -        cond do -          Ecto.get_meta(setting, :state) == :deleted -> default -          can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value) -          true -> value -        end +    Enum.each(merged, &Logger.add_backend/1) -      :ok = update_env(group, key, merged_value) +    :ok = update_env(:logger, :backends, merged) +  end -      group_for_restart(group, key, value, merged_value) +  defp configure({group, key, _, merged}) do +    merged = +      if key == :console do +        put_in(merged[:format], merged[:format] <> "\n") +      else +        merged +      end + +    backend = +      if key == :ex_syslogger, +        do: {ExSyslogger, :ex_syslogger}, +        else: key + +    Logger.configure_backend(backend, merged) +    :ok = update_env(:logger, group, merged) +  end + +  defp update({group, key, value, merged}) do +    try do +      :ok = update_env(group, key, merged) + +      if group != :pleroma or pleroma_need_restart?(group, key, value), do: group      rescue        error ->          error_msg = -          "updating env causes error, group: " <> -            inspect(setting.group) <> -            " key: " <> -            inspect(setting.key) <> -            " value: " <> -            inspect(ConfigDB.from_binary(setting.value)) <> " error: " <> inspect(error) +          "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{ +            inspect(value) +          } error: #{inspect(error)}"          Logger.warn(error_msg) @@ -133,6 +157,9 @@ defmodule Pleroma.Config.TransferTask do      end    end +  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) +    @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean()    def pleroma_need_restart?(group, key, value) do      group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value) @@ -150,9 +177,6 @@ defmodule Pleroma.Config.TransferTask do        end)    end -  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, env), do: Restarter.Pleroma.restart_after_boot(env)    defp restart(started_applications, app, _) do diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex index d9b601223..6fc47620c 100644 --- a/lib/pleroma/ecto_enums.ex +++ b/lib/pleroma/ecto_enums.ex @@ -4,10 +4,16 @@  import EctoEnum -defenum(UserRelationshipTypeEnum, +defenum(Pleroma.UserRelationship.Type,    block: 1,    mute: 2,    reblog_mute: 3,    notification_mute: 4,    inverse_subscription: 5  ) + +defenum(Pleroma.FollowingRelationship.State, +  follow_pending: 1, +  follow_accept: 2, +  follow_reject: 3 +) diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index a9538ea4e..9ccf40495 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -8,12 +8,13 @@ defmodule Pleroma.FollowingRelationship do    import Ecto.Changeset    import Ecto.Query +  alias Ecto.Changeset    alias FlakeId.Ecto.CompatType    alias Pleroma.Repo    alias Pleroma.User    schema "following_relationships" do -    field(:state, :string, default: "accept") +    field(:state, Pleroma.FollowingRelationship.State, default: :follow_pending)      belongs_to(:follower, User, type: CompatType)      belongs_to(:following, User, type: CompatType) @@ -27,6 +28,18 @@ defmodule Pleroma.FollowingRelationship do      |> put_assoc(:follower, attrs.follower)      |> put_assoc(:following, attrs.following)      |> validate_required([:state, :follower, :following]) +    |> unique_constraint(:follower_id, +      name: :following_relationships_follower_id_following_id_index +    ) +    |> validate_not_self_relationship() +  end + +  def state_to_enum(state) when state in ["pending", "accept", "reject"] do +    String.to_existing_atom("follow_#{state}") +  end + +  def state_to_enum(state) do +    raise "State is not convertible to Pleroma.FollowingRelationship.State: #{state}"    end    def get(%User{} = follower, %User{} = following) do @@ -35,7 +48,7 @@ defmodule Pleroma.FollowingRelationship do      |> Repo.one()    end -  def update(follower, following, "reject"), do: unfollow(follower, following) +  def update(follower, following, :follow_reject), do: unfollow(follower, following)    def update(%User{} = follower, %User{} = following, state) do      case get(follower, following) do @@ -50,7 +63,7 @@ defmodule Pleroma.FollowingRelationship do      end    end -  def follow(%User{} = follower, %User{} = following, state \\ "accept") do +  def follow(%User{} = follower, %User{} = following, state \\ :follow_accept) do      %__MODULE__{}      |> changeset(%{follower: follower, following: following, state: state})      |> Repo.insert(on_conflict: :nothing) @@ -80,7 +93,7 @@ defmodule Pleroma.FollowingRelationship do    def get_follow_requests(%User{id: id}) do      __MODULE__      |> join(:inner, [r], f in assoc(r, :follower)) -    |> where([r], r.state == "pending") +    |> where([r], r.state == ^:follow_pending)      |> where([r], r.following_id == ^id)      |> select([r, f], f)      |> Repo.all() @@ -88,7 +101,7 @@ defmodule Pleroma.FollowingRelationship do    def following?(%User{id: follower_id}, %User{id: followed_id}) do      __MODULE__ -    |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") +    |> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept)      |> Repo.exists?()    end @@ -97,7 +110,7 @@ defmodule Pleroma.FollowingRelationship do        __MODULE__        |> join(:inner, [r], u in User, on: r.following_id == u.id)        |> where([r], r.follower_id == ^user.id) -      |> where([r], r.state == "accept") +      |> where([r], r.state == ^:follow_accept)        |> select([r, u], u.follower_address)        |> Repo.all() @@ -157,4 +170,30 @@ defmodule Pleroma.FollowingRelationship do        fr -> fr.follower_id == follower.id and fr.following_id == following.id      end)    end + +  defp validate_not_self_relationship(%Changeset{} = changeset) do +    changeset +    |> validate_follower_id_following_id_inequality() +    |> validate_following_id_follower_id_inequality() +  end + +  defp validate_follower_id_following_id_inequality(%Changeset{} = changeset) do +    validate_change(changeset, :follower_id, fn _, follower_id -> +      if follower_id == get_field(changeset, :following_id) do +        [source_id: "can't be equal to following_id"] +      else +        [] +      end +    end) +  end + +  defp validate_following_id_follower_id_inequality(%Changeset{} = changeset) do +    validate_change(changeset, :following_id, fn _, following_id -> +      if following_id == get_field(changeset, :follower_id) do +        [target_id: "can't be equal to follower_id"] +      else +        [] +      end +    end) +  end  end diff --git a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex index 4f124ed4d..84b7c5d83 100644 --- a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex @@ -42,13 +42,13 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do      else        {:user_match, false} ->          Logger.debug("Failed to map identity from signature (payload actor mismatch)") -        Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") +        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")          assign(conn, :valid_signature, false)        # remove me once testsuite uses mapped capabilities instead of what we do now        {:user, nil} ->          Logger.debug("Failed to map identity from signature (lookup failure)") -        Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") +        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")          conn      end    end @@ -60,7 +60,7 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do      else        _ ->          Logger.debug("Failed to map identity from signature (no payload actor mismatch)") -        Logger.debug("key_id=#{key_id_from_conn(conn)}") +        Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")          assign(conn, :valid_signature, false)      end    end diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex index 1529da717..c51e2c634 100644 --- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex +++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex @@ -110,20 +110,9 @@ defmodule Pleroma.Plugs.RateLimiter do    end    def disabled?(conn) do -    localhost_or_socket = -      case Config.get([Pleroma.Web.Endpoint, :http, :ip]) do -        {127, 0, 0, 1} -> true -        {0, 0, 0, 0, 0, 0, 0, 1} -> true -        {:local, _} -> true -        _ -> false -      end - -    remote_ip_not_found = -      if Map.has_key?(conn.assigns, :remote_ip_found), -        do: !conn.assigns.remote_ip_found, -        else: false - -    localhost_or_socket and remote_ip_not_found +    if Map.has_key?(conn.assigns, :remote_ip_found), +      do: !conn.assigns.remote_ip_found, +      else: false    end    @inspect_bucket_not_found {:error, :not_found} diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex index 0ac9050d0..2eca4f8f6 100644 --- a/lib/pleroma/plugs/remote_ip.ex +++ b/lib/pleroma/plugs/remote_ip.ex @@ -7,8 +7,6 @@ defmodule Pleroma.Plugs.RemoteIp do    This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.    """ -  import Plug.Conn -    @behaviour Plug    @headers ~w[ @@ -28,12 +26,11 @@ defmodule Pleroma.Plugs.RemoteIp do    def init(_), do: nil -  def call(%{remote_ip: original_remote_ip} = conn, _) do +  def call(conn, _) do      config = Pleroma.Config.get(__MODULE__, [])      if Keyword.get(config, :enabled, false) do -      %{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts(config)) -      assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip) +      RemoteIp.call(conn, remote_ip_opts(config))      else        conn      end diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex index 36ff024a7..94147e0c4 100644 --- a/lib/pleroma/plugs/uploaded_media.ex +++ b/lib/pleroma/plugs/uploaded_media.ex @@ -41,6 +41,7 @@ defmodule Pleroma.Plugs.UploadedMedia do          conn ->            conn        end +      |> merge_resp_headers([{"content-security-policy", "sandbox"}])      config = Pleroma.Config.get(Pleroma.Upload) diff --git a/lib/pleroma/pool/connections.ex b/lib/pleroma/pool/connections.ex index 4d4ba913c..acafe1bea 100644 --- a/lib/pleroma/pool/connections.ex +++ b/lib/pleroma/pool/connections.ex @@ -243,7 +243,7 @@ defmodule Pleroma.Pool.Connections do    @impl true    def handle_info({:DOWN, _ref, :process, conn_pid, reason}, state) do -    Logger.debug("received DOWM message for #{inspect(conn_pid)} reason -> #{inspect(reason)}") +    Logger.debug("received DOWN message for #{inspect(conn_pid)} reason -> #{inspect(reason)}")      state =        with {key, conn} <- find_conn(state.conns, conn_pid) do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d05dfb480..896bab140 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -351,18 +351,26 @@ defmodule Pleroma.User do    defp fix_follower_address(params), do: params -  def remote_user_creation(params) do +  def remote_user_changeset(struct \\ %User{local: false}, params) do      bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)      name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) +    name = +      case params[:name] do +        name when is_binary(name) and byte_size(name) > 0 -> name +        _ -> params[:nickname] +      end +      params =        params +      |> Map.put(:name, name) +      |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())        |> truncate_if_exists(:name, name_limit)        |> truncate_if_exists(:bio, bio_limit)        |> truncate_fields_param()        |> fix_follower_address() -    %User{local: false} +    struct      |> cast(        params,        [ @@ -378,6 +386,7 @@ defmodule Pleroma.User do          :ap_enabled,          :banner,          :locked, +        :last_refreshed_at,          :magic_key,          :uri,          :follower_address, @@ -511,52 +520,6 @@ defmodule Pleroma.User do      end    end -  def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do -    bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) -    name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) - -    params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now()) - -    params = if remote?, do: truncate_fields_param(params), else: params - -    struct -    |> cast( -      params, -      [ -        :bio, -        :name, -        :emoji, -        :follower_address, -        :following_address, -        :public_key, -        :inbox, -        :shared_inbox, -        :avatar, -        :last_refreshed_at, -        :ap_enabled, -        :banner, -        :locked, -        :magic_key, -        :follower_count, -        :following_count, -        :hide_follows, -        :fields, -        :hide_followers, -        :allow_following_move, -        :discoverable, -        :hide_followers_count, -        :hide_follows_count, -        :actor_type, -        :also_known_as -      ] -    ) -    |> unique_constraint(:nickname) -    |> validate_format(:nickname, local_nickname_regex()) -    |> validate_length(:bio, max: bio_limit) -    |> validate_length(:name, max: name_limit) -    |> validate_fields(remote?) -  end -    def update_as_admin_changeset(struct, params) do      struct      |> update_changeset(params) @@ -726,7 +689,7 @@ defmodule Pleroma.User do    @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}    def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do -    follow(follower, followed, "pending") +    follow(follower, followed, :follow_pending)    end    def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do @@ -746,14 +709,14 @@ defmodule Pleroma.User do    def follow_all(follower, followeds) do      followeds      |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) -    |> Enum.each(&follow(follower, &1, "accept")) +    |> Enum.each(&follow(follower, &1, :follow_accept))      set_cache(follower)    end    defdelegate following(user), to: FollowingRelationship -  def follow(%User{} = follower, %User{} = followed, state \\ "accept") do +  def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do      deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])      cond do @@ -780,7 +743,7 @@ defmodule Pleroma.User do    def unfollow(%User{} = follower, %User{} = followed) do      case get_follow_state(follower, followed) do -      state when state in ["accept", "pending"] -> +      state when state in [:follow_pending, :follow_accept] ->          FollowingRelationship.unfollow(follower, followed)          {:ok, followed} = update_follower_count(followed) @@ -798,6 +761,7 @@ defmodule Pleroma.User do    defdelegate following?(follower, followed), to: FollowingRelationship +  @doc "Returns follow state as Pleroma.FollowingRelationship.State value"    def get_follow_state(%User{} = follower, %User{} = following) do      following_relationship = FollowingRelationship.get(follower, following)      get_follow_state(follower, following, following_relationship) @@ -811,8 +775,11 @@ defmodule Pleroma.User do      case {following_relationship, following.local} do        {nil, false} ->          case Utils.fetch_latest_follow(follower, following) do -          %{data: %{"state" => state}} when state in ["pending", "accept"] -> state -          _ -> nil +          %Activity{data: %{"state" => state}} when state in ["pending", "accept"] -> +            FollowingRelationship.state_to_enum(state) + +          _ -> +            nil          end        {%{state: state}, _} -> @@ -1311,7 +1278,7 @@ defmodule Pleroma.User do    def blocks?(%User{} = user, %User{} = target) do      blocks_user?(user, target) || -      (!User.following?(user, target) && blocks_domain?(user, target)) +      (blocks_domain?(user, target) and not User.following?(user, target))    end    def blocks_user?(%User{} = user, %User{} = target) do @@ -1664,17 +1631,6 @@ defmodule Pleroma.User do      end    end -  defp blank?(""), do: nil -  defp blank?(n), do: n - -  def insert_or_update_user(data) do -    data -    |> Map.put(:name, blank?(data[:name]) || data[:nickname]) -    |> remote_user_creation() -    |> Repo.insert(on_conflict: {:replace_all_except, [:id]}, conflict_target: :nickname) -    |> set_cache() -  end -    def ap_enabled?(%User{local: true}), do: true    def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled    def ap_enabled?(_), do: false diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 884e33039..ec88088cf 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -148,7 +148,7 @@ defmodule Pleroma.User.Query do        as: :relationships,        on: r.following_id == ^id and r.follower_id == u.id      ) -    |> where([relationships: r], r.state == "accept") +    |> where([relationships: r], r.state == ^:follow_accept)    end    defp compose_query({:friends, %User{id: id}}, query) do @@ -158,7 +158,7 @@ defmodule Pleroma.User.Query do        as: :relationships,        on: r.following_id == u.id and r.follower_id == ^id      ) -    |> where([relationships: r], r.state == "accept") +    |> where([relationships: r], r.state == ^:follow_accept)    end    defp compose_query({:recipients_from_activity, to}, query) do @@ -173,7 +173,7 @@ defmodule Pleroma.User.Query do      )      |> where(        [u, following: f, relationships: r], -      u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") +      u.ap_id in ^to or (f.follower_address in ^to and r.state == ^:follow_accept)      )      |> distinct(true)    end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 18a5eec72..235ad427c 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do    import Ecto.Changeset    import Ecto.Query +  alias Ecto.Changeset    alias Pleroma.FollowingRelationship    alias Pleroma.Repo    alias Pleroma.User @@ -16,12 +17,12 @@ defmodule Pleroma.UserRelationship do    schema "user_relationships" do      belongs_to(:source, User, type: FlakeId.Ecto.CompatType)      belongs_to(:target, User, type: FlakeId.Ecto.CompatType) -    field(:relationship_type, UserRelationshipTypeEnum) +    field(:relationship_type, Pleroma.UserRelationship.Type)      timestamps(updated_at: false)    end -  for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do +  for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do      # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,      #   `def create_notification_mute/2`, `def create_inverse_subscription/2`      def unquote(:"create_#{relationship_type}")(source, target), @@ -40,7 +41,7 @@ defmodule Pleroma.UserRelationship do    def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) -  def user_relationship_mappings, do: UserRelationshipTypeEnum.__enum_map__() +  def user_relationship_mappings, do: Pleroma.UserRelationship.Type.__enum_map__()    def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do      user_relationship @@ -129,17 +130,27 @@ defmodule Pleroma.UserRelationship do    end    @doc ":relationships option for StatusView / AccountView / NotificationView" -  def view_relationships_option(nil = _reading_user, _actors) do +  def view_relationships_option(reading_user, actors, opts \\ []) + +  def view_relationships_option(nil = _reading_user, _actors, _opts) do      %{user_relationships: [], following_relationships: []}    end -  def view_relationships_option(%User{} = reading_user, actors) do +  def view_relationships_option(%User{} = reading_user, actors, opts) do +    {source_to_target_rel_types, target_to_source_rel_types} = +      if opts[:source_mutes_only] do +        # This option is used for rendering statuses (FE needs `muted` flag for each one anyways) +        {[:mute], []} +      else +        {[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]} +      end +      user_relationships =        UserRelationship.dictionary(          [reading_user],          actors, -        [:block, :mute, :notification_mute, :reblog_mute], -        [:block, :inverse_subscription] +        source_to_target_rel_types, +        target_to_source_rel_types        )      following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) @@ -147,18 +158,26 @@ defmodule Pleroma.UserRelationship do      %{user_relationships: user_relationships, following_relationships: following_relationships}    end -  defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do +  defp validate_not_self_relationship(%Changeset{} = changeset) do      changeset -    |> validate_change(:target_id, fn _, target_id -> -      if target_id == get_field(changeset, :source_id) do -        [target_id: "can't be equal to source_id"] +    |> validate_source_id_target_id_inequality() +    |> validate_target_id_source_id_inequality() +  end + +  defp validate_source_id_target_id_inequality(%Changeset{} = changeset) do +    validate_change(changeset, :source_id, fn _, source_id -> +      if source_id == get_field(changeset, :target_id) do +        [source_id: "can't be equal to target_id"]        else          []        end      end) -    |> validate_change(:source_id, fn _, source_id -> -      if source_id == get_field(changeset, :target_id) do -        [source_id: "can't be equal to target_id"] +  end + +  defp validate_target_id_source_id_inequality(%Changeset{} = changeset) do +    validate_change(changeset, :target_id, fn _, target_id -> +      if target_id == get_field(changeset, :source_id) do +        [target_id: "can't be equal to source_id"]        else          []        end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9b832f4cb..35af0f7dc 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -721,7 +721,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      end    end -  defp fetch_activities_for_context_query(context, opts) do +  def fetch_activities_for_context_query(context, opts) do      public = [Constants.as_public()]      recipients = @@ -1576,11 +1576,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    def make_user_from_ap_id(ap_id) do -    if _user = User.get_cached_by_ap_id(ap_id) do +    user = User.get_cached_by_ap_id(ap_id) + +    if user && !User.ap_enabled?(user) do        Transmogrifier.upgrade_user_from_ap_id(ap_id)      else        with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do -        User.insert_or_update_user(data) +        if user do +          user +          |> User.remote_user_changeset(data) +          |> User.update_and_set_cache() +        else +          data +          |> User.remote_user_changeset() +          |> Repo.insert() +          |> User.set_cache() +        end        else          e -> {:error, e}        end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 3d4070fd5..09119137b 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -205,16 +205,46 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      |> Map.put("conversation", context)    end +  defp add_if_present(map, _key, nil), do: map + +  defp add_if_present(map, key, value) do +    Map.put(map, key, value) +  end +    def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do      attachments =        Enum.map(attachment, fn data -> -        media_type = data["mediaType"] || data["mimeType"] -        href = data["url"] || data["href"] -        url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}] +        url = +          cond do +            is_list(data["url"]) -> List.first(data["url"]) +            is_map(data["url"]) -> data["url"] +            true -> nil +          end -        data -        |> Map.put("mediaType", media_type) -        |> Map.put("url", url) +        media_type = +          cond do +            is_map(url) && is_binary(url["mediaType"]) -> url["mediaType"] +            is_binary(data["mediaType"]) -> data["mediaType"] +            is_binary(data["mimeType"]) -> data["mimeType"] +            true -> nil +          end + +        href = +          cond do +            is_map(url) && is_binary(url["href"]) -> url["href"] +            is_binary(data["url"]) -> data["url"] +            is_binary(data["href"]) -> data["href"] +          end + +        attachment_url = +          %{"href" => href} +          |> add_if_present("mediaType", media_type) +          |> add_if_present("type", Map.get(url || %{}, "type")) + +        %{"url" => [attachment_url]} +        |> add_if_present("mediaType", media_type) +        |> add_if_present("type", data["type"]) +        |> add_if_present("name", data["name"])        end)      Map.put(object, "attachment", attachments) @@ -494,7 +524,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do             {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},             {_, {:ok, _}} <-               {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, -           {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do +           {:ok, _relationship} <- +             FollowingRelationship.update(follower, followed, :follow_accept) do          ActivityPub.accept(%{            to: [follower.ap_id],            actor: followed, @@ -504,7 +535,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        else          {:user_blocked, true} ->            {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") -          {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") +          {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)            ActivityPub.reject(%{              to: [follower.ap_id], @@ -515,7 +546,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          {:follow, {:error, _}} ->            {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") -          {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") +          {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)            ActivityPub.reject(%{              to: [follower.ap_id], @@ -525,7 +556,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do            })          {:user_locked, true} -> -          {:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending") +          {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_pending)            :noop        end @@ -545,7 +576,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do           {:ok, follow_activity} <- get_follow_activity(follow_object, followed),           {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),           %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), -         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do +         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do        ActivityPub.accept(%{          to: follow_activity.data["to"],          type: "Accept", @@ -568,7 +599,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do           {:ok, follow_activity} <- get_follow_activity(follow_object, followed),           {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),           %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), -         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), +         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),           {:ok, activity} <-             ActivityPub.reject(%{               to: follow_activity.data["to"], @@ -680,7 +711,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)        actor -      |> User.upgrade_changeset(new_user_data, true) +      |> User.remote_user_changeset(new_user_data)        |> User.update_and_set_cache()        ActivityPub.update(%{ @@ -1223,12 +1254,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def upgrade_user_from_ap_id(ap_id) do      with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),           {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), -         already_ap <- User.ap_enabled?(user), -         {:ok, user} <- upgrade_user(user, data) do -      if not already_ap do -        TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) -      end - +         {:ok, user} <- update_user(user, data) do +      TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})        {:ok, user}      else        %User{} = user -> {:ok, user} @@ -1236,9 +1263,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      end    end -  defp upgrade_user(user, data) do +  defp update_user(user, data) do      user -    |> User.upgrade_changeset(data, true) +    |> User.remote_user_changeset(data)      |> User.update_and_set_cache()    end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index fdbd24acb..831c3bd02 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -258,7 +258,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      conn      |> put_view(Pleroma.Web.AdminAPI.StatusView) -    |> render("index.json", %{activities: activities, as: :activity}) +    |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})    end    def list_user_statuses(conn, %{"nickname" => nickname} = params) do @@ -277,7 +277,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do        conn        |> put_view(StatusView) -      |> render("index.json", %{activities: activities, as: :activity}) +      |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})      else        _ -> {:error, :not_found}      end @@ -812,7 +812,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      conn      |> put_view(Pleroma.Web.AdminAPI.StatusView) -    |> render("index.json", %{activities: activities, as: :activity}) +    |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})    end    def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index ca0bcebc7..d50969b2a 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -38,7 +38,12 @@ defmodule Pleroma.Web.AdminAPI.ReportView do        actor: merge_account_views(user),        content: content,        created_at: created_at, -      statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}), +      statuses: +        StatusView.render("index.json", %{ +          activities: statuses, +          as: :activity, +          skip_relationships: false +        }),        state: report.data["state"],        notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})      } diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index 41e48a085..3890489e3 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -31,7 +31,7 @@ defmodule Pleroma.Web.ApiSpec do                password: %OpenApiSpex.OAuthFlow{                  authorizationUrl: "/oauth/authorize",                  tokenUrl: "/oauth/token", -                scopes: %{"read" => "read"} +                scopes: %{"read" => "read", "write" => "write", "follow" => "follow"}                }              }            } diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index 35cf4c0d8..7348dcbee 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -4,7 +4,7 @@  defmodule Pleroma.Web.ApiSpec.Helpers do    def request_body(description, schema_ref, opts \\ []) do -    media_types = ["application/json", "multipart/form-data"] +    media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]      content =        media_types diff --git a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex new file mode 100644 index 000000000..dd14837c3 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex @@ -0,0 +1,64 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do +  alias OpenApiSpex.Operation +  alias OpenApiSpex.Schema +  alias Pleroma.Web.ApiSpec.Helpers +  alias Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest +  alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def index_operation do +    %Operation{ +      tags: ["domain_blocks"], +      summary: "Fetch domain blocks", +      description: "View domains the user has blocked.", +      security: [%{"oAuth" => ["follow", "read:blocks"]}], +      operationId: "DomainBlockController.index", +      responses: %{ +        200 => Operation.response("Domain blocks", "application/json", DomainBlocksResponse) +      } +    } +  end + +  def create_operation do +    %Operation{ +      tags: ["domain_blocks"], +      summary: "Block a domain", +      description: """ +      Block a domain to: + +      - hide all public posts from it +      - hide all notifications from it +      - remove all followers from it +      - prevent following new users from it (but does not remove existing follows) +      """, +      operationId: "DomainBlockController.create", +      requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true), +      security: [%{"oAuth" => ["follow", "write:blocks"]}], +      responses: %{ +        200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) +      } +    } +  end + +  def delete_operation do +    %Operation{ +      tags: ["domain_blocks"], +      summary: "Unblock a domain", +      description: "Remove a domain block, if it exists in the user's array of blocked domains.", +      operationId: "DomainBlockController.delete", +      requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true), +      security: [%{"oAuth" => ["follow", "write:blocks"]}], +      responses: %{ +        200 => Operation.response("Empty object", "application/json", %Schema{type: :object}) +      } +    } +  end +end diff --git a/lib/pleroma/web/api_spec/schemas/domain_block_request.ex b/lib/pleroma/web/api_spec/schemas/domain_block_request.ex new file mode 100644 index 000000000..ee9238361 --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/domain_block_request.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.Web.ApiSpec.Schemas.DomainBlockRequest do +  alias OpenApiSpex.Schema +  require OpenApiSpex + +  OpenApiSpex.schema(%{ +    title: "DomainBlockRequest", +    type: :object, +    properties: %{ +      domain: %Schema{type: :string} +    }, +    required: [:domain], +    example: %{ +      "domain" => "facebook.com" +    } +  }) +end diff --git a/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex b/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex new file mode 100644 index 000000000..d895aca4e --- /dev/null +++ b/lib/pleroma/web/api_spec/schemas/domain_blocks_response.ex @@ -0,0 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse do +  require OpenApiSpex +  alias OpenApiSpex.Schema + +  OpenApiSpex.schema(%{ +    title: "DomainBlocksResponse", +    description: "Response schema for domain blocks", +    type: :array, +    items: %Schema{type: :string}, +    example: ["google.com", "facebook.com"] +  }) +end diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index c4356f93b..c1cd15bb2 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -187,7 +187,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do    end    defp preview?(draft) do -    preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) || false +    preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"])      %__MODULE__{draft | preview?: preview?}    end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 952a8d8cb..f50a909aa 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -45,7 +45,7 @@ defmodule Pleroma.Web.CommonAPI do      with {:ok, follower} <- User.follow(follower, followed),           %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),           {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), -         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), +         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept),           {:ok, _activity} <-             ActivityPub.accept(%{               to: [follower.ap_id], @@ -60,7 +60,7 @@ defmodule Pleroma.Web.CommonAPI do    def reject_follow_request(follower, followed) do      with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),           {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), -         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), +         {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),           {:ok, _activity} <-             ActivityPub.reject(%{               to: [follower.ap_id], diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index b49523ec3..4780081b2 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -5,10 +5,18 @@  defmodule Pleroma.Web.ControllerHelper do    use Pleroma.Web, :controller -  # As in MastoAPI, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html +  alias Pleroma.Config + +  # As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html    @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"] -  def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil -  def truthy_param?(value), do: value not in @falsy_param_values + +  def explicitly_falsy_param?(value), do: value in @falsy_param_values + +  # Note: `nil` and `""` are considered falsy values in Pleroma +  def falsy_param?(value), +    do: explicitly_falsy_param?(value) or value in [nil, ""] + +  def truthy_param?(value), do: not falsy_param?(value)    def json_response(conn, status, json) do      conn @@ -96,4 +104,14 @@ defmodule Pleroma.Web.ControllerHelper do    def put_if_exist(map, _key, nil), do: map    def put_if_exist(map, key, value), do: Map.put(map, key, value) + +  @doc "Whether to skip rendering `[:account][:pleroma][:relationship]`for statuses/notifications" +  def skip_relationships?(params) do +    if Config.get([:extensions, :output_relationships_in_statuses_by_default]) do +      false +    else +      # BREAKING: older PleromaFE versions do not send this param but _do_ expect relationships. +      not truthy_param?(params["with_relationships"]) +    end +  end  end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 3fcaa6be6..a42e6b463 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -6,7 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do    use Pleroma.Web, :controller    import Pleroma.Web.ControllerHelper, -    only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3] +    only: [ +      add_link_headers: 2, +      truthy_param?: 1, +      assign_account_by_id: 2, +      json_response: 3, +      skip_relationships?: 1 +    ]    alias Pleroma.Plugs.OAuthScopesPlug    alias Pleroma.Plugs.RateLimiter @@ -233,7 +239,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do        conn        |> add_link_headers(activities)        |> put_view(StatusView) -      |> render("index.json", activities: activities, for: reading_user, as: :activity) +      |> render("index.json", +        activities: activities, +        for: reading_user, +        as: :activity, +        skip_relationships: skip_relationships?(params) +      )      else        _e -> render_error(conn, :not_found, "Can't find user")      end diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index e4156cbe6..84de79413 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -8,6 +8,9 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do    alias Pleroma.Plugs.OAuthScopesPlug    alias Pleroma.User +  plug(OpenApiSpex.Plug.CastAndValidate) +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation +    plug(      OAuthScopesPlug,      %{scopes: ["follow", "read:blocks"]} when action == :index @@ -26,13 +29,13 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do    end    @doc "POST /api/v1/domain_blocks" -  def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do +  def create(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do      User.block_domain(blocker, domain)      json(conn, %{})    end    @doc "DELETE /api/v1/domain_blocks" -  def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do +  def delete(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do      User.unblock_domain(blocker, domain)      json(conn, %{})    end diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index a6b4096ec..7fb536b09 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -5,7 +5,7 @@  defmodule Pleroma.Web.MastodonAPI.NotificationController do    use Pleroma.Web, :controller -  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] +  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]    alias Pleroma.Notification    alias Pleroma.Plugs.OAuthScopesPlug @@ -45,7 +45,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do      conn      |> add_link_headers(notifications) -    |> render("index.json", notifications: notifications, for: user) +    |> render("index.json", +      notifications: notifications, +      for: user, +      skip_relationships: skip_relationships?(params) +    )    end    # GET /api/v1/notifications/:id diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index fcab4ef63..c258742dd 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -5,13 +5,14 @@  defmodule Pleroma.Web.MastodonAPI.SearchController do    use Pleroma.Web, :controller +  import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 2, skip_relationships?: 1] +    alias Pleroma.Activity    alias Pleroma.Plugs.OAuthScopesPlug    alias Pleroma.Plugs.RateLimiter    alias Pleroma.Repo    alias Pleroma.User    alias Pleroma.Web -  alias Pleroma.Web.ControllerHelper    alias Pleroma.Web.MastodonAPI.AccountView    alias Pleroma.Web.MastodonAPI.StatusView @@ -66,10 +67,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do    defp search_options(params, user) do      [ +      skip_relationships: skip_relationships?(params),        resolve: params["resolve"] == "true",        following: params["following"] == "true", -      limit: ControllerHelper.fetch_integer_param(params, "limit"), -      offset: ControllerHelper.fetch_integer_param(params, "offset"), +      limit: fetch_integer_param(params, "limit"), +      offset: fetch_integer_param(params, "offset"),        type: params["type"],        author: get_author(params),        for_user: user @@ -79,12 +81,24 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do    defp resource_search(_, "accounts", query, options) do      accounts = with_fallback(fn -> User.search(query, options) end) -    AccountView.render("index.json", users: accounts, for: options[:for_user], as: :user) + +    AccountView.render("index.json", +      users: accounts, +      for: options[:for_user], +      as: :user, +      skip_relationships: false +    )    end    defp resource_search(_, "statuses", query, options) do      statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end) -    StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity) + +    StatusView.render("index.json", +      activities: statuses, +      for: options[:for_user], +      as: :activity, +      skip_relationships: options[:skip_relationships] +    )    end    defp resource_search(:v2, "hashtags", query, _options) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index ec8f0d8a0..397dd10e3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -5,7 +5,8 @@  defmodule Pleroma.Web.MastodonAPI.StatusController do    use Pleroma.Web, :controller -  import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2] +  import Pleroma.Web.ControllerHelper, +    only: [try_render: 3, add_link_headers: 2, skip_relationships?: 1]    require Ecto.Query @@ -101,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    `ids` query param is required    """ -  def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do +  def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do      limit = 100      activities = @@ -110,7 +111,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do        |> Activity.all_by_ids_with_object()        |> Enum.filter(&Visibility.visible_for_user?(&1, user)) -    render(conn, "index.json", activities: activities, for: user, as: :activity) +    render(conn, "index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    @doc """ @@ -360,7 +366,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do      conn      |> add_link_headers(activities) -    |> render("index.json", activities: activities, for: user, as: :activity) +    |> render("index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    @doc "GET /api/v1/bookmarks" @@ -378,6 +389,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do      conn      |> add_link_headers(bookmarks) -    |> render("index.json", %{activities: activities, for: user, as: :activity}) +    |> render("index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end  end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 91f41416d..b3c58005e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    use Pleroma.Web, :controller    import Pleroma.Web.ControllerHelper, -    only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1] +    only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1]    alias Pleroma.Pagination    alias Pleroma.Plugs.OAuthScopesPlug @@ -14,9 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub -  # TODO: Replace with a macro when there is a Phoenix release with +  # TODO: Replace with a macro when there is a Phoenix release with the following commit in it:    # 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) @@ -49,7 +48,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do      conn      |> add_link_headers(activities) -    |> render("index.json", activities: activities, for: user, as: :activity) +    |> render("index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    # GET /api/v1/timelines/direct @@ -68,7 +72,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do      conn      |> add_link_headers(activities) -    |> render("index.json", activities: activities, for: user, as: :activity) +    |> render("index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    # GET /api/v1/timelines/public @@ -95,7 +104,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do        conn        |> add_link_headers(activities, %{"local" => local_only}) -      |> render("index.json", activities: activities, for: user, as: :activity) +      |> render("index.json", +        activities: activities, +        for: user, +        as: :activity, +        skip_relationships: skip_relationships?(params) +      )      else        render_error(conn, :unauthorized, "authorization required for timeline view")      end @@ -140,7 +154,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do      conn      |> add_link_headers(activities, %{"local" => local_only}) -    |> render("index.json", activities: activities, for: user, as: :activity) +    |> render("index.json", +      activities: activities, +      for: user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    # GET /api/v1/timelines/list/:list_id @@ -164,7 +183,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do          |> ActivityPub.fetch_activities_bounded(following, params)          |> Enum.reverse() -      render(conn, "index.json", activities: activities, for: user, as: :activity) +      render(conn, "index.json", +        activities: activities, +        for: user, +        as: :activity, +        skip_relationships: skip_relationships?(params) +      )      else        _e -> render_error(conn, :forbidden, "Error.")      end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 966032b69..b4b61e74c 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do    def render("index.json", %{users: users} = opts) do      reading_user = opts[:for] +    # Note: :skip_relationships option is currently intentionally not supported for accounts      relationships_opt =        cond do          Map.has_key?(opts, :relationships) -> @@ -73,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do      followed_by =        if following_relationships do          case FollowingRelationship.find(following_relationships, target, reading_user) do -          %{state: "accept"} -> true +          %{state: :follow_accept} -> true            _ -> false          end        else @@ -83,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do      # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags      %{        id: to_string(target.id), -      following: follow_state == "accept", +      following: follow_state == :follow_accept,        followed_by: followed_by,        blocking:          UserRelationship.exists?( @@ -125,7 +126,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do            reading_user,            &User.subscribed_to?(&2, &1)          ), -      requested: follow_state == "pending", +      requested: follow_state == :follow_pending,        domain_blocking: User.blocks_domain?(reading_user, target),        showing_reblogs:          not UserRelationship.exists?( @@ -190,11 +191,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do        end)      relationship = -      render("relationship.json", %{ -        user: opts[:for], -        target: user, -        relationships: opts[:relationships] -      }) +      if opts[:skip_relationships] do +        %{} +      else +        render("relationship.json", %{ +          user: opts[:for], +          target: user, +          relationships: opts[:relationships] +        }) +      end      %{        id: to_string(user.id), diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index ae87d4701..734ffbf39 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -51,14 +51,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do              |> Enum.filter(& &1)              |> Kernel.++(move_activities_targets) -          UserRelationship.view_relationships_option(reading_user, actors) +          UserRelationship.view_relationships_option(reading_user, actors, +            source_mutes_only: opts[:skip_relationships] +          )        end -    opts = %{ -      for: reading_user, -      parent_activities: parent_activities, -      relationships: relationships_opt -    } +    opts = +      opts +      |> Map.put(:parent_activities, parent_activities) +      |> Map.put(:relationships, relationships_opt)      safe_render_many(notifications, NotificationView, "show.json", opts)    end @@ -82,12 +83,16 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do      mastodon_type = Activity.mastodon_notification_type(activity) +    render_opts = %{ +      relationships: opts[:relationships], +      skip_relationships: opts[:skip_relationships] +    } +      with %{id: _} = account <- -           AccountView.render("show.json", %{ -             user: actor, -             for: reading_user, -             relationships: opts[:relationships] -           }) do +           AccountView.render( +             "show.json", +             Map.merge(render_opts, %{user: actor, for: reading_user}) +           ) do        response = %{          id: to_string(notification.id),          type: mastodon_type, @@ -98,8 +103,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do          }        } -      render_opts = %{relationships: opts[:relationships]} -        case mastodon_type do          "mention" ->            put_status(response, activity, reading_user, render_opts) @@ -111,6 +114,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do            put_status(response, parent_activity_fn.(), reading_user, render_opts)          "move" -> +          # Note: :skip_relationships option being applied to _account_ rendering (here)            put_target(response, activity, reading_user, render_opts)          "follow" -> diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index cea76e735..b5850e1ae 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -99,7 +99,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          true ->            actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"])) -          UserRelationship.view_relationships_option(reading_user, actors) +          UserRelationship.view_relationships_option(reading_user, actors, +            source_mutes_only: opts[:skip_relationships] +          )        end      opts = @@ -153,7 +155,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          AccountView.render("show.json", %{            user: user,            for: opts[:for], -          relationships: opts[:relationships] +          relationships: opts[:relationships], +          skip_relationships: opts[:skip_relationships]          }),        in_reply_to_id: nil,        in_reply_to_account_id: nil, @@ -301,6 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          _ -> []        end +    # Status muted state (would do 1 request per status unless user mutes are preloaded)      muted =        thread_muted? ||          UserRelationship.exists?( @@ -319,7 +323,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          AccountView.render("show.json", %{            user: user,            for: opts[:for], -          relationships: opts[:relationships] +          relationships: opts[:relationships], +          skip_relationships: opts[:skip_relationships]          }),        in_reply_to_id: reply_to && to_string(reply_to.id),        in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id), diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index ed4fdfdba..60405fbff 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do    use Pleroma.Web, :controller    import Pleroma.Web.ControllerHelper, -    only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] +    only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]    alias Ecto.Changeset    alias Pleroma.Plugs.OAuthScopesPlug @@ -132,7 +132,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do      conn      |> add_link_headers(activities)      |> put_view(StatusView) -    |> render("index.json", activities: activities, for: for_user, as: :activity) +    |> render("index.json", +      activities: activities, +      for: for_user, +      as: :activity, +      skip_relationships: skip_relationships?(params) +    )    end    @doc "POST /api/v1/pleroma/accounts/:id/subscribe" 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 dae7f0f2f..d4c5c5925 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -5,7 +5,7 @@  defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do    use Pleroma.Web, :controller -  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] +  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]    alias Pleroma.Activity    alias Pleroma.Conversation.Participation @@ -110,12 +110,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do    end    def conversation_statuses( -        %{assigns: %{user: user}} = conn, +        %{assigns: %{user: %{id: user_id} = user}} = conn,          %{"id" => participation_id} = params        ) do -    with %Participation{} = participation <- -           Participation.get(participation_id, preload: [:conversation]), -         true <- user.id == participation.user_id do +    with %Participation{user_id: ^user_id} = participation <- +           Participation.get(participation_id, preload: [:conversation]) do        params =          params          |> Map.put("blocking_user", user) @@ -124,13 +123,19 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do        activities =          participation.conversation.ap_id -        |> ActivityPub.fetch_activities_for_context(params) +        |> ActivityPub.fetch_activities_for_context_query(params) +        |> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))          |> Enum.reverse()        conn        |> add_link_headers(activities)        |> put_view(StatusView) -      |> render("index.json", %{activities: activities, for: user, as: :activity}) +      |> render("index.json", +        activities: activities, +        for: user, +        as: :activity, +        skip_relationships: skip_relationships?(params) +      )      else        _error ->          conn @@ -184,13 +189,17 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do      end    end -  def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do +  def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do      with notifications <- Notification.set_read_up_to(user, max_id) do        notifications = Enum.take(notifications, 80)        conn        |> put_view(NotificationView) -      |> render("index.json", %{notifications: notifications, for: user}) +      |> render("index.json", +        notifications: notifications, +        for: user, +        skip_relationships: skip_relationships?(params) +      )      end    end  end diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 0314535d2..9d3d7f978 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -64,5 +64,8 @@ defmodule Pleroma.Web.RichMedia.Helpers do    def fetch_data_for_activity(_), do: %{} -  def perform(:fetch, %Activity{} = activity), do: fetch_data_for_activity(activity) +  def perform(:fetch, %Activity{} = activity) do +    fetch_data_for_activity(activity) +    :ok +  end  end @@ -117,7 +117,7 @@ defmodule Pleroma.Mixfile do        {:ecto_enum, "~> 1.4"},        {:ecto_sql, "~> 3.3.2"},        {:postgrex, ">= 0.13.5"}, -      {:oban, "~> 0.12.1"}, +      {:oban, "~> 1.2"},        {:gettext, "~> 0.15"},        {:comeonin, "~> 4.1.1"},        {:pbkdf2_elixir, "~> 0.12.3"}, @@ -183,7 +183,7 @@ defmodule Pleroma.Mixfile do        {:flake_id, "~> 0.1.0"},        {:remote_ip,         git: "https://git.pleroma.social/pleroma/remote_ip.git", -       ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, +       ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},        {:captcha,         git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",         ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, @@ -26,7 +26,7 @@    "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},    "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},    "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, -  "ecto": {:hex, :ecto, "3.3.3", "0830bf3aebcbf3d8c1a1811cd581773b6866886c012f52c0f027031fa96a0b53", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "12e368e3c2a2938d7776defaabdae40e82900fc4d8d66120ec1e01dfd8b93c3a"}, +  "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"},    "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},    "ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},    "esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"}, @@ -55,7 +55,7 @@    "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},    "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},    "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, -  "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, +  "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},    "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},    "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},    "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, @@ -73,7 +73,7 @@    "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},    "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},    "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, -  "oban": {:hex, :oban, "0.12.1", "695e9490c6e0edfca616d80639528e448bd29b3bff7b7dd10a56c79b00a5d7fb", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c1d58d69b8b5a86e7167abbb8cc92764a66f25f12f6172052595067fc6a30a17"}, +  "oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},    "open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},    "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},    "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"}, @@ -97,7 +97,7 @@    "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "d736bfa7444112eb840027bb887832a0e403a4a3437f48028c3b29a2dbbd2543"},    "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},    "recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"}, -  "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "825dc00aaba5a1b7c4202a532b696b595dd3bcb3", [ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"]}, +  "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},    "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},    "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},    "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, diff --git a/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs b/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs index c618ea381..b6f0ac66b 100644 --- a/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs +++ b/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs @@ -3,7 +3,6 @@ defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do    import Ecto.Query    alias Pleroma.Activity    alias Pleroma.Bookmark -  alias Pleroma.User    alias Pleroma.Repo    def up do diff --git a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs index 2f336a5e8..43d616705 100644 --- a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs +++ b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs @@ -1,6 +1,5 @@  defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do    use Ecto.Migration -  alias Pleroma.User    def change do      execute(""" diff --git a/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs b/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs new file mode 100644 index 000000000..2b0820f3f --- /dev/null +++ b/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs @@ -0,0 +1,29 @@ +defmodule Pleroma.Repo.Migrations.ChangeFollowingRelationshipsStateToInteger do +  use Ecto.Migration + +  @alter_following_relationship_state "ALTER TABLE following_relationships ALTER COLUMN state" + +  def up do +    execute(""" +    #{@alter_following_relationship_state} TYPE integer USING +    CASE +      WHEN state = 'pending' THEN 1 +      WHEN state = 'accept' THEN 2 +      WHEN state = 'reject' THEN 3 +      ELSE 0 +    END; +    """) +  end + +  def down do +    execute(""" +    #{@alter_following_relationship_state} TYPE varchar(255) USING +    CASE +      WHEN state = 1 THEN 'pending' +      WHEN state = 2 THEN 'accept' +      WHEN state = 3 THEN 'reject' +      ELSE '' +    END; +    """) +  end +end diff --git a/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs b/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs new file mode 100644 index 000000000..884832f84 --- /dev/null +++ b/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddFollowingRelationshipsFollowingIdIndex do +  use Ecto.Migration + +  # [:follower_index] index is useless because of [:follower_id, :following_id] index +  # [:following_id] index makes sense because of user's followers-targeted queries +  def change do +    drop_if_exists(index(:following_relationships, [:follower_id])) + +    create_if_not_exists(index(:following_relationships, [:following_id])) +  end +end diff --git a/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs b/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs new file mode 100644 index 000000000..e7ff04008 --- /dev/null +++ b/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.UpdateObanJobsTable do +  use Ecto.Migration + +  def up do +    Oban.Migrations.up(version: 8) +  end + +  def down do +    Oban.Migrations.down(version: 7) +  end +end diff --git a/test/following_relationship_test.exs b/test/following_relationship_test.exs index 865bb3838..17a468abb 100644 --- a/test/following_relationship_test.exs +++ b/test/following_relationship_test.exs @@ -15,28 +15,28 @@ defmodule Pleroma.FollowingRelationshipTest do      test "returns following addresses without internal.fetch" do        user = insert(:user)        fetch_actor = InternalFetchActor.get_actor() -      FollowingRelationship.follow(fetch_actor, user, "accept") +      FollowingRelationship.follow(fetch_actor, user, :follow_accept)        assert FollowingRelationship.following(fetch_actor) == [user.follower_address]      end      test "returns following addresses without relay" do        user = insert(:user)        relay_actor = Relay.get_actor() -      FollowingRelationship.follow(relay_actor, user, "accept") +      FollowingRelationship.follow(relay_actor, user, :follow_accept)        assert FollowingRelationship.following(relay_actor) == [user.follower_address]      end      test "returns following addresses without remote user" do        user = insert(:user)        actor = insert(:user, local: false) -      FollowingRelationship.follow(actor, user, "accept") +      FollowingRelationship.follow(actor, user, :follow_accept)        assert FollowingRelationship.following(actor) == [user.follower_address]      end      test "returns following addresses with local user" do        user = insert(:user)        actor = insert(:user, local: true) -      FollowingRelationship.follow(actor, user, "accept") +      FollowingRelationship.follow(actor, user, :follow_accept)        assert FollowingRelationship.following(actor) == [                 actor.follower_address, diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs index 0ce9f3a0a..4d3d694f4 100644 --- a/test/plugs/rate_limiter_test.exs +++ b/test/plugs/rate_limiter_test.exs @@ -5,8 +5,10 @@  defmodule Pleroma.Plugs.RateLimiterTest do    use Pleroma.Web.ConnCase +  alias Phoenix.ConnTest    alias Pleroma.Config    alias Pleroma.Plugs.RateLimiter +  alias Plug.Conn    import Pleroma.Factory    import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] @@ -36,8 +38,15 @@ defmodule Pleroma.Plugs.RateLimiterTest do    end    test "it is disabled if it remote ip plug is enabled but no remote ip is found" do -    Config.put([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1}) -    assert RateLimiter.disabled?(Plug.Conn.assign(build_conn(), :remote_ip_found, false)) +    assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false)) +  end + +  test "it is enabled if remote ip found" do +    refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true)) +  end + +  test "it is enabled if remote_ip_found flag doesn't exist" do +    refute RateLimiter.disabled?(build_conn())    end    test "it restricts based on config values" do @@ -58,7 +67,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do      end      conn = RateLimiter.call(conn, plug_opts) -    assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) +    assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)      assert conn.halted      Process.sleep(50) @@ -68,7 +77,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do      conn = RateLimiter.call(conn, plug_opts)      assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) -    refute conn.status == Plug.Conn.Status.code(:too_many_requests) +    refute conn.status == Conn.Status.code(:too_many_requests)      refute conn.resp_body      refute conn.halted    end @@ -98,7 +107,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do        plug_opts = RateLimiter.init(name: limiter_name, params: ["id"])        conn = build_conn(:get, "/?id=1") -      conn = Plug.Conn.fetch_query_params(conn) +      conn = Conn.fetch_query_params(conn)        conn_2 = build_conn(:get, "/?id=2")        RateLimiter.call(conn, plug_opts) @@ -119,7 +128,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do        id = "100"        conn = build_conn(:get, "/?id=#{id}") -      conn = Plug.Conn.fetch_query_params(conn) +      conn = Conn.fetch_query_params(conn)        conn_2 = build_conn(:get, "/?id=#{101}")        RateLimiter.call(conn, plug_opts) @@ -147,13 +156,13 @@ defmodule Pleroma.Plugs.RateLimiterTest do        conn = RateLimiter.call(conn, plug_opts) -      assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) +      assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)        assert conn.halted        conn_2 = RateLimiter.call(conn_2, plug_opts)        assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) -      refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) +      refute conn_2.status == Conn.Status.code(:too_many_requests)        refute conn_2.resp_body        refute conn_2.halted      end @@ -187,7 +196,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do        conn = RateLimiter.call(conn, plug_opts) -      assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) +      assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)        assert conn.halted      end @@ -210,12 +219,12 @@ defmodule Pleroma.Plugs.RateLimiterTest do        end        conn = RateLimiter.call(conn, plug_opts) -      assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) +      assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)        assert conn.halted        conn_2 = RateLimiter.call(conn_2, plug_opts)        assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) -      refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) +      refute conn_2.status == Conn.Status.code(:too_many_requests)        refute conn_2.resp_body        refute conn_2.halted      end diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index b45f37263..8df835b56 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -140,7 +140,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do      test "user is unsubscribed" do        followed = insert(:user)        user = insert(:user) -      User.follow(user, followed, "accept") +      User.follow(user, followed, :follow_accept)        Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) diff --git a/test/user_test.exs b/test/user_test.exs index d35005353..65e118d6d 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -194,7 +194,8 @@ defmodule Pleroma.UserTest do      CommonAPI.follow(pending_follower, locked)      CommonAPI.follow(pending_follower, locked)      CommonAPI.follow(accepted_follower, locked) -    Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept") + +    Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept)      assert [^pending_follower] = User.get_follow_requests(locked)    end @@ -319,7 +320,7 @@ defmodule Pleroma.UserTest do            following_address: "http://localhost:4001/users/fuser2/following"          }) -      {:ok, user} = User.follow(user, followed, "accept") +      {:ok, user} = User.follow(user, followed, :follow_accept)        {:ok, user, _activity} = User.unfollow(user, followed) @@ -332,7 +333,7 @@ defmodule Pleroma.UserTest do        followed = insert(:user)        user = insert(:user) -      {:ok, user} = User.follow(user, followed, "accept") +      {:ok, user} = User.follow(user, followed, :follow_accept)        assert User.following(user) == [user.follower_address, followed.follower_address] @@ -353,7 +354,7 @@ defmodule Pleroma.UserTest do    test "test if a user is following another user" do      followed = insert(:user)      user = insert(:user) -    User.follow(user, followed, "accept") +    User.follow(user, followed, :follow_accept)      assert User.following?(user, followed)      refute User.following?(followed, user) @@ -609,7 +610,7 @@ defmodule Pleroma.UserTest do               ) <> "/followers"    end -  describe "remote user creation changeset" do +  describe "remote user changeset" do      @valid_remote %{        bio: "hello",        name: "Someone", @@ -621,28 +622,28 @@ defmodule Pleroma.UserTest do      setup do: clear_config([:instance, :user_name_length])      test "it confirms validity" do -      cs = User.remote_user_creation(@valid_remote) +      cs = User.remote_user_changeset(@valid_remote)        assert cs.valid?      end      test "it sets the follower_adress" do -      cs = User.remote_user_creation(@valid_remote) +      cs = User.remote_user_changeset(@valid_remote)        # remote users get a fake local follower address        assert cs.changes.follower_address ==                 User.ap_followers(%User{nickname: @valid_remote[:nickname]})      end      test "it enforces the fqn format for nicknames" do -      cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"}) +      cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"})        assert Ecto.Changeset.get_field(cs, :local) == false        assert cs.changes.avatar        refute cs.valid?      end      test "it has required fields" do -      [:name, :ap_id] +      [:ap_id]        |> Enum.each(fn field -> -        cs = User.remote_user_creation(Map.delete(@valid_remote, field)) +        cs = User.remote_user_changeset(Map.delete(@valid_remote, field))          refute cs.valid?        end)      end @@ -1198,58 +1199,6 @@ defmodule Pleroma.UserTest do      assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")    end -  describe "insert or update a user from given data" do -    test "with normal data" do -      user = insert(:user, %{nickname: "nick@name.de"}) -      data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname} - -      assert {:ok, %User{}} = User.insert_or_update_user(data) -    end - -    test "with overly long fields" do -      current_max_length = Pleroma.Config.get([:instance, :account_field_value_length], 255) -      user = insert(:user, nickname: "nickname@supergood.domain") - -      data = %{ -        ap_id: user.ap_id, -        name: user.name, -        nickname: user.nickname, -        fields: [ -          %{"name" => "myfield", "value" => String.duplicate("h", current_max_length + 1)} -        ] -      } - -      assert {:ok, %User{}} = User.insert_or_update_user(data) -    end - -    test "with an overly long bio" do -      current_max_length = Pleroma.Config.get([:instance, :user_bio_length], 5000) -      user = insert(:user, nickname: "nickname@supergood.domain") - -      data = %{ -        ap_id: user.ap_id, -        name: user.name, -        nickname: user.nickname, -        bio: String.duplicate("h", current_max_length + 1) -      } - -      assert {:ok, %User{}} = User.insert_or_update_user(data) -    end - -    test "with an overly long display name" do -      current_max_length = Pleroma.Config.get([:instance, :user_name_length], 100) -      user = insert(:user, nickname: "nickname@supergood.domain") - -      data = %{ -        ap_id: user.ap_id, -        name: String.duplicate("h", current_max_length + 1), -        nickname: user.nickname -      } - -      assert {:ok, %User{}} = User.insert_or_update_user(data) -    end -  end -    describe "per-user rich-text filtering" do      test "html_filter_policy returns default policies, when rich-text is enabled" do        user = insert(:user) diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 573853afa..fbacb3993 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -1239,16 +1239,56 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do          filename: "an_image.jpg"        } -      conn = +      object =          conn          |> assign(:user, user)          |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) +        |> json_response(:created) -      assert object = json_response(conn, :created)        assert object["name"] == desc        assert object["type"] == "Document"        assert object["actor"] == user.ap_id +      assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] +      assert is_binary(object_href) +      assert object_mediatype == "image/jpeg" + +      activity_request = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "type" => "Create", +        "object" => %{ +          "type" => "Note", +          "content" => "AP C2S test, attachment", +          "attachment" => [object] +        }, +        "to" => "https://www.w3.org/ns/activitystreams#Public", +        "cc" => [] +      } + +      activity_response = +        conn +        |> assign(:user, user) +        |> post("/users/#{user.nickname}/outbox", activity_request) +        |> json_response(:created) + +      assert activity_response["id"] +      assert activity_response["object"] +      assert activity_response["actor"] == user.ap_id + +      assert %Object{data: %{"attachment" => [attachment]}} = +               Object.normalize(activity_response["object"]) + +      assert attachment["type"] == "Document" +      assert attachment["name"] == desc + +      assert [ +               %{ +                 "href" => ^object_href, +                 "type" => "Link", +                 "mediaType" => ^object_mediatype +               } +             ] = attachment["url"] +      # Fails if unauthenticated        conn        |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})        |> json_response(403) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 8ddc75669..6057e360a 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1230,19 +1230,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        attachment = %{          "type" => "Link",          "mediaType" => "video/mp4", -        "href" => -          "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", -        "mimeType" => "video/mp4", -        "size" => 5_015_880,          "url" => [            %{              "href" =>                "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", -            "mediaType" => "video/mp4", -            "type" => "Link" +            "mediaType" => "video/mp4"            } -        ], -        "width" => 480 +        ]        }        assert object.data["url"] == @@ -1624,7 +1618,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do          })        user_two = insert(:user) -      Pleroma.FollowingRelationship.follow(user_two, user, "accept") +      Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)        {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})        {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) @@ -2063,11 +2057,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do                   %{                     "mediaType" => "video/mp4",                     "url" => [ -                     %{ -                       "href" => "https://peertube.moe/stat-480.mp4", -                       "mediaType" => "video/mp4", -                       "type" => "Link" -                     } +                     %{"href" => "https://peertube.moe/stat-480.mp4", "mediaType" => "video/mp4"}                     ]                   }                 ] @@ -2085,23 +2075,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do                   %{                     "mediaType" => "video/mp4",                     "url" => [ -                     %{ -                       "href" => "https://pe.er/stat-480.mp4", -                       "mediaType" => "video/mp4", -                       "type" => "Link" -                     } +                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}                     ]                   },                   %{ -                   "href" => "https://pe.er/stat-480.mp4",                     "mediaType" => "video/mp4", -                   "mimeType" => "video/mp4",                     "url" => [ -                     %{ -                       "href" => "https://pe.er/stat-480.mp4", -                       "mediaType" => "video/mp4", -                       "type" => "Link" -                     } +                     %{"href" => "https://pe.er/stat-480.mp4", "mediaType" => "video/mp4"}                     ]                   }                 ] diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 20578161b..8d00893a5 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do      {:ok, user} =        insert(:user) -      |> User.upgrade_changeset(%{fields: fields}) +      |> User.update_changeset(%{fields: fields})        |> User.update_and_set_cache()      assert %{ diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index f02f6ae7a..60ec895f5 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2273,13 +2273,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do            value: :erlang.term_to_binary([])          ) +      Pleroma.Config.TransferTask.load_and_update_env([], false) + +      assert Application.get_env(:logger, :backends) == [] +        conn =          post(conn, "/api/pleroma/admin/config", %{            configs: [              %{                group: config.group,                key: config.key, -              value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}] +              value: [":console"]              }            ]          }) @@ -2290,8 +2294,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                     "group" => ":logger",                     "key" => ":backends",                     "value" => [ -                     ":console", -                     %{"tuple" => ["ExSyslogger", ":ex_syslogger"]} +                     ":console"                     ],                     "db" => [":backends"]                   } @@ -2299,14 +2302,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do               }        assert Application.get_env(:logger, :backends) == [ -               :console, -               {ExSyslogger, :ex_syslogger} +               :console               ] - -      capture_log(fn -> -        require Logger -        Logger.warn("Ooops...") -      end) =~ "Ooops..."      end      test "saving full setting if value is not keyword", %{conn: conn} do diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 5e78c5758..e130736ec 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -553,7 +553,7 @@ defmodule Pleroma.Web.CommonAPITest do        assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =                 CommonAPI.follow(follower, followed) -      assert User.get_follow_state(follower, followed) == "pending" +      assert User.get_follow_state(follower, followed) == :follow_pending        assert {:ok, follower} = CommonAPI.unfollow(follower, followed)        assert User.get_follow_state(follower, followed) == nil @@ -575,7 +575,7 @@ defmodule Pleroma.Web.CommonAPITest do        assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =                 CommonAPI.follow(follower, followed) -      assert User.get_follow_state(follower, followed) == "pending" +      assert User.get_follow_state(follower, followed) == :follow_pending        assert {:ok, follower} = CommonAPI.unfollow(follower, followed)        assert User.get_follow_state(follower, followed) == nil diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/web/mastodon_api/controllers/domain_block_controller_test.exs index 8d24b3b88..d66190c90 100644 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -6,20 +6,29 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do    use Pleroma.Web.ConnCase    alias Pleroma.User +  alias Pleroma.Web.ApiSpec +  alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse    import Pleroma.Factory +  import OpenApiSpex.TestAssertions    test "blocking / unblocking a domain" do      %{user: user, conn: conn} = oauth_access(["write:blocks"])      other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) -    ret_conn = post(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) +    ret_conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})      assert %{} = json_response(ret_conn, 200)      user = User.get_cached_by_ap_id(user.ap_id)      assert User.blocks?(user, other_user) -    ret_conn = delete(conn, "/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) +    ret_conn = +      conn +      |> put_req_header("content-type", "application/json") +      |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})      assert %{} = json_response(ret_conn, 200)      user = User.get_cached_by_ap_id(user.ap_id) @@ -41,5 +50,12 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do      assert "bad.site" in domain_blocks      assert "even.worse.site" in domain_blocks +    assert_schema(domain_blocks, "DomainBlocksResponse", ApiSpec.spec()) +  end + +  test "DomainBlocksResponse example matches schema" do +    api_spec = ApiSpec.spec() +    schema = DomainBlocksResponse.schema() +    assert_schema(schema.example, "DomainBlocksResponse", api_spec)    end  end diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index dd848821a..d8dbe4800 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do        other_user = insert(:user)        {:ok, _activity} = ActivityPub.follow(other_user, user) -      {:ok, other_user} = User.follow(other_user, user, "pending") +      {:ok, other_user} = User.follow(other_user, user, :follow_pending)        assert User.following?(other_user, user) == false @@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do        other_user = insert(:user)        {:ok, _activity} = ActivityPub.follow(other_user, user) -      {:ok, other_user} = User.follow(other_user, user, "pending") +      {:ok, other_user} = User.follow(other_user, user, :follow_pending)        user = User.get_cached_by_id(user.id)        other_user = User.get_cached_by_id(other_user.id) diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index 1557937d8..8c815b415 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -12,6 +12,26 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do    import Pleroma.Factory +  test "does NOT render account/pleroma/relationship if this is disabled by default" do +    clear_config([:extensions, :output_relationships_in_statuses_by_default], false) + +    %{user: user, conn: conn} = oauth_access(["read:notifications"]) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, [_notification]} = Notification.create_notifications(activity) + +    response = +      conn +      |> assign(:user, user) +      |> get("/api/v1/notifications") +      |> json_response(200) + +    assert Enum.all?(response, fn n -> +             get_in(n, ["account", "pleroma", "relationship"]) == %{} +           end) +  end +    test "list of notifications" do      %{user: user, conn: conn} = oauth_access(["read:notifications"])      other_user = insert(:user) diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index cd9ca4973..162f7b1b2 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -1047,6 +1047,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do    end    test "bookmarks" do +    bookmarks_uri = "/api/v1/bookmarks?with_relationships=true" +      %{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])      author = insert(:user) @@ -1068,7 +1070,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do      assert json_response(response2, 200)["bookmarked"] == true -    bookmarks = get(conn, "/api/v1/bookmarks") +    bookmarks = get(conn, bookmarks_uri)      assert [json_response(response2, 200), json_response(response1, 200)] ==               json_response(bookmarks, 200) @@ -1077,7 +1079,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do      assert json_response(response1, 200)["bookmarked"] == false -    bookmarks = get(conn, "/api/v1/bookmarks") +    bookmarks = get(conn, bookmarks_uri)      assert [json_response(response2, 200)] == json_response(bookmarks, 200)    end diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 97b1c3e66..06efdc901 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -20,7 +20,30 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do    describe "home" do      setup do: oauth_access(["read:statuses"]) +    test "does NOT render account/pleroma/relationship if this is disabled by default", %{ +      user: user, +      conn: conn +    } do +      clear_config([:extensions, :output_relationships_in_statuses_by_default], false) + +      other_user = insert(:user) + +      {:ok, _} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +      response = +        conn +        |> assign(:user, user) +        |> get("/api/v1/timelines/home") +        |> json_response(200) + +      assert Enum.all?(response, fn n -> +               get_in(n, ["account", "pleroma", "relationship"]) == %{} +             end) +    end +      test "the home timeline", %{user: user, conn: conn} do +      uri = "/api/v1/timelines/home?with_relationships=true" +        following = insert(:user, nickname: "followed")        third_user = insert(:user, nickname: "repeated") @@ -28,13 +51,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        {:ok, activity} = CommonAPI.post(third_user, %{"status" => "repeated post"})        {:ok, _, _} = CommonAPI.repeat(activity.id, following) -      ret_conn = get(conn, "/api/v1/timelines/home") +      ret_conn = get(conn, uri)        assert Enum.empty?(json_response(ret_conn, :ok))        {:ok, _user} = User.follow(user, following) -      ret_conn = get(conn, "/api/v1/timelines/home") +      ret_conn = get(conn, uri)        assert [                 %{ @@ -59,7 +82,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do        {:ok, _user} = User.follow(third_user, user) -      ret_conn = get(conn, "/api/v1/timelines/home") +      ret_conn = get(conn, uri)        assert [                 %{ diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 32250f06f..8bf7eb3be 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -169,6 +169,23 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do      id_one = activity.id      id_two = activity_two.id      assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result + +    {:ok, %{id: id_three}} = +      CommonAPI.post(other_user, %{ +        "status" => "Bye!", +        "in_reply_to_status_id" => activity.id, +        "in_reply_to_conversation_id" => participation.id +      }) + +    assert [%{"id" => ^id_two}, %{"id" => ^id_three}] = +             conn +             |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2") +             |> json_response(:ok) + +    assert [%{"id" => ^id_three}] = +             conn +             |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}") +             |> json_response(:ok)    end    test "PATCH /api/v1/pleroma/conversations/:id" do diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index b3fe22920..eb082b79f 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -209,7 +209,7 @@ defmodule Pleroma.Web.StreamerTest do        Pleroma.Config.put([:instance, :skip_thread_containment], false)        author = insert(:user)        user = insert(:user) -      User.follow(user, author, "accept") +      User.follow(user, author, :follow_accept)        activity =          insert(:note_activity, @@ -232,7 +232,7 @@ defmodule Pleroma.Web.StreamerTest do        Pleroma.Config.put([:instance, :skip_thread_containment], true)        author = insert(:user)        user = insert(:user) -      User.follow(user, author, "accept") +      User.follow(user, author, :follow_accept)        activity =          insert(:note_activity, @@ -255,7 +255,7 @@ defmodule Pleroma.Web.StreamerTest do        Pleroma.Config.put([:instance, :skip_thread_containment], false)        author = insert(:user)        user = insert(:user, skip_thread_containment: true) -      User.follow(user, author, "accept") +      User.follow(user, author, :follow_accept)        activity =          insert(:note_activity, | 
