import Ecto.Query
require Logger
+ require Pleroma.Constants
- @supported_object_types ["Article", "Note", "Video", "Page", "Question"]
+ @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]
+ @supported_report_states ~w(open closed resolved)
+ @valid_visibilities ~w(public unlisted private direct)
# Some implementations send the actor URI as the actor field, others send the entire actor object,
# so figure out what the actor's URI is based on what we have.
- def get_ap_id(object) do
- case object do
- %{"id" => id} -> id
- id -> id
- end
- end
+ def get_ap_id(%{"id" => id} = _), do: id
+ def get_ap_id(id), do: id
def normalize_params(params) do
Map.put(params, "actor", get_ap_id(params["actor"]))
def create_context(context) do
context = context || generate_id("contexts")
- changeset = Object.context_mapping(context)
- case Repo.insert(changeset) do
- {:ok, object} ->
- object
+ # Ecto has problems accessing the constraint inside the jsonb,
+ # so we explicitly check for the existed object before insert
+ object = Object.get_cached_by_ap_id(context)
- # This should be solved by an upsert, but it seems ecto
- # has problems accessing the constraint inside the jsonb.
- {:error, _} ->
- Object.get_cached_by_ap_id(context)
+ with true <- is_nil(object),
+ changeset <- Object.context_mapping(context),
+ {:ok, inserted_object} <- Repo.insert(changeset) do
+ inserted_object
+ else
+ _ ->
+ object
end
end
Enqueues an activity for federation if it's local
"""
def maybe_federate(%Activity{local: true} = activity) do
- priority =
- case activity.data["type"] do
- "Delete" -> 10
- "Create" -> 1
- _ -> 5
- end
+ if Pleroma.Config.get!([:instance, :federating]) do
+ priority =
+ case activity.data["type"] do
+ "Delete" -> 10
+ "Create" -> 1
+ _ -> 5
+ end
+
+ Pleroma.Web.Federator.publish(activity, priority)
+ end
- Pleroma.Web.Federator.publish(activity, priority)
:ok
end
def insert_full_object(map), do: {:ok, map, nil}
- def update_object_in_activities(%{data: %{"id" => id}} = object) do
- # TODO
- # Update activities that already had this. Could be done in a seperate process.
- # Alternatively, just don't do this and fetch the current object each time. Most
- # could probably be taken from cache.
- relevant_activities = Activity.get_all_create_by_object_ap_id(id)
-
- Enum.map(relevant_activities, fn activity ->
- new_activity_data = activity.data |> Map.put("object", object.data)
- changeset = Changeset.change(activity, data: new_activity_data)
- Repo.update(changeset)
- end)
- end
-
#### Like-related helpers
@doc """
|> Map.put("#{property}_count", length(element))
|> Map.put("#{property}s", element),
changeset <- Changeset.change(object, data: new_data),
- {:ok, object} <- Object.update_and_set_cache(changeset),
- _ <- update_object_in_activities(object) do
+ {:ok, object} <- Object.update_and_set_cache(changeset) do
{:ok, object}
end
end
@doc """
Updates a follow activity's state (for locked accounts).
"""
- def update_follow_state(
- %Activity{data: %{"actor" => actor, "object" => object, "state" => "pending"}} = activity,
+ def update_follow_state_for_all(
+ %Activity{data: %{"actor" => actor, "object" => object}} = activity,
state
) do
try do
"type" => "Follow",
"actor" => follower_id,
"to" => [followed_id],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"object" => followed_id,
"state" => "pending"
}
"actor" => ap_id,
"object" => id,
"to" => [user.follower_address, object.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => object.data["context"]
}
"actor" => ap_id,
"object" => activity.data,
"to" => [user.follower_address, activity.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => context
}
"actor" => ap_id,
"object" => activity.data,
"to" => [user.follower_address, activity.data["actor"]],
- "cc" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [Pleroma.Constants.as_public()],
"context" => context
}
def add_announce_to_object(
%Activity{
- data: %{"actor" => actor, "cc" => ["https://www.w3.org/ns/activitystreams#Public"]}
+ data: %{"actor" => actor, "cc" => [Pleroma.Constants.as_public()]}
},
object
) do
"actor" => params.actor.ap_id,
"content" => params.content,
"object" => object,
- "context" => params.context
+ "context" => params.context,
+ "state" => "open"
}
|> Map.merge(additional)
end
"""
def fetch_ordered_collection(from, pages_left, acc \\ []) do
with {:ok, response} <- Tesla.get(from),
- {:ok, collection} <- Poison.decode(response.body) do
+ {:ok, collection} <- Jason.decode(response.body) do
case collection["type"] do
"OrderedCollection" ->
# If we've encountered the OrderedCollection and not the page,
end
end
end
+
+ #### Report-related helpers
+
+ def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
+ with new_data <- Map.put(activity.data, "state", state),
+ changeset <- Changeset.change(activity, data: new_data),
+ {:ok, activity} <- Repo.update(changeset) do
+ {:ok, activity}
+ end
+ end
+
+ def update_report_state(_, _), do: {:error, "Unsupported state"}
+
+ def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do
+ [to, cc, recipients] =
+ activity
+ |> get_updated_targets(visibility)
+ |> Enum.map(&Enum.uniq/1)
+
+ object_data =
+ activity.object.data
+ |> Map.put("to", to)
+ |> Map.put("cc", cc)
+
+ {:ok, object} =
+ activity.object
+ |> Object.change(%{data: object_data})
+ |> Object.update_and_set_cache()
+
+ activity_data =
+ activity.data
+ |> Map.put("to", to)
+ |> Map.put("cc", cc)
+
+ activity
+ |> Map.put(:object, object)
+ |> Activity.change(%{data: activity_data, recipients: recipients})
+ |> Repo.update()
+ end
+
+ def update_activity_visibility(_, _), do: {:error, "Unsupported visibility"}
+
+ defp get_updated_targets(
+ %Activity{data: %{"to" => to} = data, recipients: recipients},
+ visibility
+ ) do
+ cc = Map.get(data, "cc", [])
+ follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address
+ public = Pleroma.Constants.as_public()
+
+ case visibility do
+ "public" ->
+ to = [public | List.delete(to, follower_address)]
+ cc = [follower_address | List.delete(cc, public)]
+ recipients = [public | recipients]
+ [to, cc, recipients]
+
+ "private" ->
+ to = [follower_address | List.delete(to, public)]
+ cc = List.delete(cc, public)
+ recipients = List.delete(recipients, public)
+ [to, cc, recipients]
+
+ "unlisted" ->
+ to = [follower_address | List.delete(to, public)]
+ cc = [public | List.delete(cc, follower_address)]
+ recipients = recipients ++ [follower_address, public]
+ [to, cc, recipients]
+
+ _ ->
+ [to, cc, recipients]
+ end
+ end
+
+ def get_existing_votes(actor, %{data: %{"id" => id}}) do
+ query =
+ from(
+ [activity, object: object] in Activity.with_preloaded_object(Activity),
+ where: fragment("(?)->>'type' = 'Create'", activity.data),
+ where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
+ where:
+ fragment(
+ "(?)->>'inReplyTo' = ?",
+ object.data,
+ ^to_string(id)
+ ),
+ where: fragment("(?)->>'type' = 'Answer'", object.data)
+ )
+
+ Repo.all(query)
+ end
end