diff options
| author | lain <lain@soykaf.club> | 2020-04-15 14:41:43 +0200 | 
|---|---|---|
| committer | lain <lain@soykaf.club> | 2020-04-15 14:41:43 +0200 | 
| commit | 44bfb491eae00d541e6d11c8b52b5de5bc0bd34e (patch) | |
| tree | 0fae87b11ceb4696075927c7f77228e59bb6926c /lib | |
| parent | 64c78581fe397b6d9356c52cf3f43becd2ff3b4e (diff) | |
| parent | ad8630b95a691d01ec49344fd1a7578860728d63 (diff) | |
| download | pleroma-44bfb491eae00d541e6d11c8b52b5de5bc0bd34e.tar.gz pleroma-44bfb491eae00d541e6d11c8b52b5de5bc0bd34e.zip | |
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
Diffstat (limited to 'lib')
36 files changed, 515 insertions, 162 deletions
| 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/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 71c8c3a4e..670ce397b 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -700,7 +700,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 @@ -720,14 +720,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 @@ -754,7 +754,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) @@ -772,6 +772,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) @@ -785,8 +786,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}, _} -> @@ -1285,7 +1289,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 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 0b4892501..4a56beb73 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -722,7 +722,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 = diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index becc35ea3..ad77a5037 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -206,16 +206,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) @@ -495,7 +525,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, @@ -505,7 +536,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], @@ -516,7 +547,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], @@ -526,7 +557,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 @@ -546,7 +577,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", @@ -569,7 +600,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"], 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 39e15adbf..2f13daf0c 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -68,7 +68,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], @@ -83,7 +83,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 21bc3d5a5..7da1a11f6 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 @@ -237,7 +243,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 0c9218454..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 @@ -66,7 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do      json(conn, %{})    end -  # POST /api/v1/notifications/dismiss +  # POST /api/v1/notifications/:id/dismiss +  # POST /api/v1/notifications/dismiss (deprecated)    def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do      with {:ok, _notif} <- Notification.dismiss(user, id) do        json(conn, %{}) 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 99e62f580..8fb96a22a 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?( @@ -192,11 +193,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 dcba67d03..9d0b3b1e4 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 @@ -139,7 +139,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 diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ce69725dc..b10bf4466 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -361,9 +361,11 @@ defmodule Pleroma.Web.Router do      get("/notifications", NotificationController, :index)      get("/notifications/:id", NotificationController, :show) +    post("/notifications/:id/dismiss", NotificationController, :dismiss)      post("/notifications/clear", NotificationController, :clear) -    post("/notifications/dismiss", NotificationController, :dismiss)      delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) +    # Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead +    post("/notifications/dismiss", NotificationController, :dismiss)      get("/scheduled_statuses", ScheduledActivityController, :index)      get("/scheduled_statuses/:id", ScheduledActivityController, :show) | 
