X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fobject.ex;h=c8d339c190ba048bc2248f29dfac1807c634cbd9;hb=eae991b06a22bf7fc3eef7a0d5b409c931c1f6cb;hp=193ae3fa884335b85e602348b9567c354a46c39f;hpb=037fefe21871ee0a1926286c87c334ea1d339394;p=akkoma diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 193ae3fa8..c8d339c19 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Object do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.Object.Fetcher alias Pleroma.ObjectTombstone alias Pleroma.Repo alias Pleroma.User @@ -34,42 +35,55 @@ defmodule Pleroma.Object do |> unique_constraint(:ap_id, name: :objects_unique_apid_index) end + def get_by_id(nil), do: nil + def get_by_id(id), do: Repo.get(Object, id) + def get_by_ap_id(nil), do: nil def get_by_ap_id(ap_id) do Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) end + defp warn_on_no_object_preloaded(ap_id) do + "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object" + |> Logger.debug() + + Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + end + + def normalize(_, fetch_remote \\ true, options \\ []) + # If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # Use this whenever possible, especially when walking graphs in an O(N) loop! - def normalize(%Activity{object: %Object{} = object}), do: object - - # Catch and log Object.normalize() calls where the Activity's child object is not - # preloaded. - def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do - Logger.debug( - "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" - ) + def normalize(%Object{} = object, _, _), do: object + def normalize(%Activity{object: %Object{} = object}, _, _), do: object - Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + # A hack for fake activities + def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _, _) do + %Object{id: "pleroma:fake_object_id", data: data} + end - normalize(ap_id) + # No preloaded object + def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do + warn_on_no_object_preloaded(ap_id) + normalize(ap_id, fetch_remote) end - def normalize(%Activity{data: %{"object" => ap_id}}) do - Logger.debug( - "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" - ) + # No preloaded object + def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do + warn_on_no_object_preloaded(ap_id) + normalize(ap_id, fetch_remote) + end - Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + # Old way, try fetching the object through cache. + def normalize(%{"id" => ap_id}, fetch_remote, _), do: normalize(ap_id, fetch_remote) + def normalize(ap_id, false, _) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) - normalize(ap_id) + def normalize(ap_id, true, options) when is_binary(ap_id) do + Fetcher.fetch_object_from_id!(ap_id, options) end - # Old way, try fetching the object through cache. - def normalize(%{"id" => ap_id}), do: normalize(ap_id) - def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) - def normalize(_), do: nil + def normalize(_, _, _), do: nil # Owned objects can only be mutated by their owner def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}), @@ -121,6 +135,13 @@ defmodule Pleroma.Object do end end + def prune(%Object{data: %{"id" => id}} = object) do + with {:ok, object} <- Repo.delete(object), + {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do + {:ok, object} + end + end + def set_cache(%Object{data: %{"id" => ap_id}} = object) do Cachex.put(:object_cache, "object:#{ap_id}", object) {:ok, object} @@ -133,4 +154,80 @@ defmodule Pleroma.Object do e -> e end end + + def increase_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def decrease_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def increase_vote_count(ap_id, name) do + with %Object{} = object <- Object.normalize(ap_id), + "Question" <- object.data["type"] do + multiple = Map.has_key?(object.data, "anyOf") + + options = + (object.data["anyOf"] || object.data["oneOf"] || []) + |> Enum.map(fn + %{"name" => ^name} = option -> + Kernel.update_in(option["replies"]["totalItems"], &(&1 + 1)) + + option -> + option + end) + + data = + if multiple do + Map.put(object.data, "anyOf", options) + else + Map.put(object.data, "oneOf", options) + end + + object + |> Object.change(%{data: data}) + |> update_and_set_cache() + else + _ -> :noop + end + end end