aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/database.ex28
-rw-r--r--lib/mix/tasks/pleroma/user.ex19
-rw-r--r--lib/pleroma/activity.ex21
-rw-r--r--lib/pleroma/bbs/handler.ex1
-rw-r--r--lib/pleroma/conversation.ex21
-rw-r--r--lib/pleroma/conversation/participation.ex10
-rw-r--r--lib/pleroma/emails/admin_email.ex2
-rw-r--r--lib/pleroma/filter.ex3
-rw-r--r--lib/pleroma/user.ex27
-rw-r--r--lib/pleroma/user/info.ex2
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex43
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex78
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex35
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex81
-rw-r--r--lib/pleroma/web/admin_api/views/report_view.ex41
-rw-r--r--lib/pleroma/web/common_api/common_api.ex57
-rw-r--r--lib/pleroma/web/common_api/utils.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex1
-rw-r--r--lib/pleroma/web/router.ex8
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex4
20 files changed, 425 insertions, 67 deletions
diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex
index ab9a3a7ff..f650b447d 100644
--- a/lib/mix/tasks/pleroma/database.ex
+++ b/lib/mix/tasks/pleroma/database.ex
@@ -4,6 +4,9 @@
defmodule Mix.Tasks.Pleroma.Database do
alias Mix.Tasks.Pleroma.Common
+ alias Pleroma.Conversation
+ alias Pleroma.Repo
+ alias Pleroma.User
require Logger
use Mix.Task
@@ -19,6 +22,14 @@ defmodule Mix.Tasks.Pleroma.Database do
Options:
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
+
+ ## Create a conversation for all existing DMs. Can be safely re-run.
+
+ mix pleroma.database bump_all_conversations
+
+ ## Remove duplicated items from following and update followers count for all users
+
+ mix pleroma.database update_users_following_followers_counts
"""
def run(["remove_embedded_objects" | args]) do
{options, [], []} =
@@ -32,7 +43,7 @@ defmodule Mix.Tasks.Pleroma.Database do
Common.start_pleroma()
Logger.info("Removing embedded objects")
- Pleroma.Repo.query!(
+ Repo.query!(
"update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
[],
timeout: :infinity
@@ -41,11 +52,24 @@ defmodule Mix.Tasks.Pleroma.Database do
if Keyword.get(options, :vacuum) do
Logger.info("Runnning VACUUM FULL")
- Pleroma.Repo.query!(
+ Repo.query!(
"vacuum full;",
[],
timeout: :infinity
)
end
end
+
+ def run(["bump_all_conversations"]) do
+ Common.start_pleroma()
+ Conversation.bump_for_all_activities()
+ end
+
+ def run(["update_users_following_followers_counts"]) do
+ Common.start_pleroma()
+
+ users = Repo.all(User)
+ Enum.each(users, &User.remove_duplicated_following/1)
+ Enum.each(users, &User.update_follower_count/1)
+ end
end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index d130ff8c9..25fc40ea7 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -77,6 +77,10 @@ defmodule Mix.Tasks.Pleroma.User do
## Delete tags from a user.
mix pleroma.user untag NICKNAME TAGS
+
+ ## Toggle confirmation of the user's account.
+
+ mix pleroma.user toggle_confirmed NICKNAME
"""
def run(["new", nickname, email | rest]) do
{options, [], []} =
@@ -388,6 +392,21 @@ defmodule Mix.Tasks.Pleroma.User do
end
end
+ def run(["toggle_confirmed", nickname]) do
+ Common.start_pleroma()
+
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
+ {:ok, user} = User.toggle_confirmation(user)
+
+ message = if user.info.confirmation_pending, do: "needs", else: "doesn't need"
+
+ Mix.shell().info("#{nickname} #{message} confirmation.")
+ else
+ _ ->
+ Mix.shell().error("No local user #{nickname}")
+ end
+ end
+
defp set_moderator(user, value) do
info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 4a0919478..4e54b15ba 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -60,21 +60,24 @@ defmodule Pleroma.Activity do
timestamps()
end
- def with_preloaded_object(query) do
- query
- |> join(
- :inner,
- [activity],
- o in Object,
+ def with_joined_object(query) do
+ join(query, :inner, [activity], o in Object,
on:
fragment(
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
o.data,
activity.data,
activity.data
- )
+ ),
+ as: :object
)
- |> preload([activity, object], object: object)
+ end
+
+ def with_preloaded_object(query) do
+ query
+ |> has_named_binding?(:object)
+ |> if(do: query, else: with_joined_object(query))
+ |> preload([activity, object: object], object: object)
end
def with_preloaded_bookmark(query, %User{} = user) do
@@ -108,7 +111,7 @@ defmodule Pleroma.Activity do
def change(struct, params \\ %{}) do
struct
- |> cast(params, [:data])
+ |> cast(params, [:data, :recipients])
|> validate_required([:data])
|> unique_constraint(:ap_id, name: :activities_unique_apid_index)
end
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex
index 106fe5d18..f34be961f 100644
--- a/lib/pleroma/bbs/handler.ex
+++ b/lib/pleroma/bbs/handler.ex
@@ -95,7 +95,6 @@ defmodule Pleroma.BBS.Handler do
activities =
[user.ap_id | user.following]
|> ActivityPub.fetch_activities(params)
- |> ActivityPub.contain_timeline(user)
Enum.each(activities, fn activity ->
puts_activity(activity)
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index 0db195988..238c1acf2 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -45,7 +45,7 @@ defmodule Pleroma.Conversation do
2. Create a participation for all the people involved who don't have one already
3. Bump all relevant participations to 'unread'
"""
- def create_or_bump_for(activity) do
+ def create_or_bump_for(activity, opts \\ []) do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
"Create" <- activity.data["type"],
object <- Pleroma.Object.normalize(activity),
@@ -58,7 +58,7 @@ defmodule Pleroma.Conversation do
participations =
Enum.map(users, fn user ->
{:ok, participation} =
- Participation.create_for_user_and_conversation(user, conversation)
+ Participation.create_for_user_and_conversation(user, conversation, opts)
participation
end)
@@ -72,4 +72,21 @@ defmodule Pleroma.Conversation do
e -> {:error, e}
end
end
+
+ @doc """
+ This is only meant to be run by a mix task. It creates conversations/participations for all direct messages in the database.
+ """
+ def bump_for_all_activities do
+ stream =
+ Pleroma.Web.ActivityPub.ActivityPub.fetch_direct_messages_query()
+ |> Repo.stream()
+
+ Repo.transaction(
+ fn ->
+ stream
+ |> Enum.each(fn a -> create_or_bump_for(a, read: true) end)
+ end,
+ timeout: :infinity
+ )
+ end
end
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 61021fb18..2a11f9069 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -22,15 +22,17 @@ defmodule Pleroma.Conversation.Participation do
def creation_cng(struct, params) do
struct
- |> cast(params, [:user_id, :conversation_id])
+ |> cast(params, [:user_id, :conversation_id, :read])
|> validate_required([:user_id, :conversation_id])
end
- def create_for_user_and_conversation(user, conversation) do
+ def create_for_user_and_conversation(user, conversation, opts \\ []) do
+ read = !!opts[:read]
+
%__MODULE__{}
- |> creation_cng(%{user_id: user.id, conversation_id: conversation.id})
+ |> creation_cng(%{user_id: user.id, conversation_id: conversation.id, read: read})
|> Repo.insert(
- on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]],
+ on_conflict: [set: [read: read, updated_at: NaiveDateTime.utc_now()]],
returning: true,
conflict_target: [:user_id, :conversation_id]
)
diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex
index df0f72f96..d0e254362 100644
--- a/lib/pleroma/emails/admin_email.ex
+++ b/lib/pleroma/emails/admin_email.ex
@@ -29,7 +29,7 @@ defmodule Pleroma.Emails.AdminEmail do
end
statuses_html =
- if length(statuses) > 0 do
+ if is_list(statuses) && length(statuses) > 0 do
statuses_list_html =
statuses
|> Enum.map(fn
diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex
index 79efc29f0..90457dadf 100644
--- a/lib/pleroma/filter.ex
+++ b/lib/pleroma/filter.ex
@@ -38,7 +38,8 @@ defmodule Pleroma.Filter do
query =
from(
f in Pleroma.Filter,
- where: f.user_id == ^user_id
+ where: f.user_id == ^user_id,
+ order_by: [desc: :id]
)
Repo.all(query)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index c6a562a61..9ffb61300 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -166,7 +166,7 @@ defmodule Pleroma.User do
def update_changeset(struct, params \\ %{}) do
struct
- |> cast(params, [:bio, :name, :avatar])
+ |> cast(params, [:bio, :name, :avatar, :following])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
@@ -709,6 +709,18 @@ defmodule Pleroma.User do
end
end
+ def remove_duplicated_following(%User{following: following} = user) do
+ uniq_following = Enum.uniq(following)
+
+ if length(following) == length(uniq_following) do
+ {:ok, user}
+ else
+ user
+ |> update_changeset(%{following: uniq_following})
+ |> update_and_set_cache()
+ end
+ end
+
@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do
criteria = %{ap_id: ap_ids, deactivated: false}
@@ -1378,4 +1390,17 @@ defmodule Pleroma.User do
def showing_reblogs?(%User{} = user, %User{} = target) do
target.ap_id not in user.info.muted_reblogs
end
+
+ @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
+ def toggle_confirmation(%User{} = user) do
+ need_confirmation? = !user.info.confirmation_pending
+
+ info_changeset =
+ User.Info.confirmation_changeset(user.info, need_confirmation: need_confirmation?)
+
+ user
+ |> change()
+ |> put_embed(:info, info_changeset)
+ |> update_and_set_cache()
+ end
end
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 5a50ee639..5f0cefc00 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -212,7 +212,7 @@ defmodule Pleroma.User.Info do
])
end
- @spec confirmation_changeset(Info.t(), keyword()) :: Ecto.Changerset.t()
+ @spec confirmation_changeset(Info.t(), keyword()) :: Changeset.t()
def confirmation_changeset(info, opts) do
need_confirmation? = Keyword.get(opts, :need_confirmation)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 233fee4fa..5c3156978 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -540,8 +540,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
)
)
- Ecto.Adapters.SQL.to_sql(:all, Repo, query)
-
query
else
Logger.error("Could not restrict visibility to #{visibility}")
@@ -557,8 +555,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
)
- Ecto.Adapters.SQL.to_sql(:all, Repo, query)
-
query
end
@@ -569,6 +565,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_visibility(query, _visibility), do: query
+ defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}) do
+ query =
+ from(
+ a in query,
+ where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
+ )
+
+ query
+ end
+
+ defp restrict_thread_visibility(query, _), do: query
+
def fetch_user_activities(user, reading_user, params \\ %{}) do
params =
params
@@ -695,6 +703,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_type(query, _), do: query
+ defp restrict_state(query, %{"state" => state}) do
+ from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
+ end
+
+ defp restrict_state(query, _), do: query
+
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
from(
activity in query,
@@ -750,8 +764,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
blocks = info.blocks || []
domain_blocks = info.domain_blocks || []
+ query =
+ if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
+
from(
- activity in query,
+ [activity, object: o] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
where: fragment("not (? && ?)", activity.recipients, ^blocks),
where:
@@ -761,7 +778,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.data,
^blocks
),
- where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
+ where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
+ where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
)
end
@@ -843,11 +861,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_local(opts)
|> restrict_actor(opts)
|> restrict_type(opts)
+ |> restrict_state(opts)
|> restrict_favorited_by(opts)
|> restrict_blocked(opts)
|> restrict_muted(opts)
|> restrict_media(opts)
|> restrict_visibility(opts)
+ |> restrict_thread_visibility(opts)
|> restrict_replies(opts)
|> restrict_reblogs(opts)
|> restrict_pinned(opts)
@@ -966,11 +986,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
contain_broken_threads(activity, user)
end
- # do post-processing on a timeline
- def contain_timeline(timeline, user) do
- timeline
- |> Enum.filter(fn activity ->
- contain_activity(activity, user)
- end)
+ def fetch_direct_messages_query do
+ Activity
+ |> restrict_type(%{"type" => "Create"})
+ |> restrict_visibility(%{visibility: "direct"})
+ |> order_by([activity], asc: activity.id)
end
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 236d1b4ac..ca8a0844b 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -20,6 +20,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger
@supported_object_types ["Article", "Note", "Video", "Page"]
+ @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.
@@ -670,7 +672,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"actor" => params.actor.ap_id,
"content" => params.content,
"object" => object,
- "context" => params.context
+ "context" => params.context,
+ "state" => "open"
}
|> Map.merge(additional)
end
@@ -713,4 +716,77 @@ defmodule Pleroma.Web.ActivityPub.Utils do
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 = "https://www.w3.org/ns/activitystreams#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
end
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index b38ee0442..93b50ee47 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -1,6 +1,7 @@
defmodule Pleroma.Web.ActivityPub.Visibility do
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.Repo
alias Pleroma.User
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
@@ -13,11 +14,12 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
end
def is_private?(activity) do
- unless is_public?(activity) do
- follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address
- Enum.any?(activity.data["to"], &(&1 == follower_address))
+ with false <- is_public?(activity),
+ %User{follower_address: follower_address} <-
+ User.get_cached_by_ap_id(activity.data["actor"]) do
+ follower_address in activity.data["to"]
else
- false
+ _ -> false
end
end
@@ -38,25 +40,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
end
- # guard
- def entire_thread_visible_for_user?(nil, _user), do: false
+ def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
+ {:ok, %{rows: [[result]]}} =
+ Ecto.Adapters.SQL.query(Repo, "SELECT thread_visibility($1, $2)", [
+ user.ap_id,
+ activity.data["id"]
+ ])
- # XXX: Probably even more inefficient than the previous implementation intended to be a placeholder untill https://git.pleroma.social/pleroma/pleroma/merge_requests/971 is in develop
- # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
-
- def entire_thread_visible_for_user?(
- %Activity{} = tail,
- # %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
- user
- ) do
- case Object.normalize(tail) do
- %{data: %{"inReplyTo" => parent_id}} when is_binary(parent_id) ->
- parent = Activity.get_in_reply_to_activity(tail)
- visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
-
- _ ->
- visible_for_user?(tail, user)
- end
+ result
end
def get_visibility(object) do
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index e00b33aba..de2a13c01 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -4,11 +4,16 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
use Pleroma.Web, :controller
+ alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.UserInviteToken
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.AdminAPI.AccountView
+ alias Pleroma.Web.AdminAPI.ReportView
alias Pleroma.Web.AdminAPI.Search
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.MastodonAPI.StatusView
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
@@ -287,12 +292,88 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(token.token)
end
+ def list_reports(conn, params) do
+ params =
+ params
+ |> Map.put("type", "Flag")
+ |> Map.put("skip_preload", true)
+
+ reports =
+ []
+ |> ActivityPub.fetch_activities(params)
+ |> Enum.reverse()
+
+ conn
+ |> put_view(ReportView)
+ |> render("index.json", %{reports: reports})
+ end
+
+ def report_show(conn, %{"id" => id}) do
+ with %Activity{} = report <- Activity.get_by_id(id) do
+ conn
+ |> put_view(ReportView)
+ |> render("show.json", %{report: report})
+ else
+ _ -> {:error, :not_found}
+ end
+ end
+
+ def report_update_state(conn, %{"id" => id, "state" => state}) do
+ with {:ok, report} <- CommonAPI.update_report_state(id, state) do
+ conn
+ |> put_view(ReportView)
+ |> render("show.json", %{report: report})
+ end
+ end
+
+ def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
+ with false <- is_nil(params["status"]),
+ %Activity{} <- Activity.get_by_id(id) do
+ params =
+ params
+ |> Map.put("in_reply_to_status_id", id)
+ |> Map.put("visibility", "direct")
+
+ {:ok, activity} = CommonAPI.post(user, params)
+
+ conn
+ |> put_view(StatusView)
+ |> render("status.json", %{activity: activity})
+ else
+ true ->
+ {:param_cast, nil}
+
+ nil ->
+ {:error, :not_found}
+ end
+ end
+
+ def status_update(conn, %{"id" => id} = params) do
+ with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
+ conn
+ |> put_view(StatusView)
+ |> render("status.json", %{activity: activity})
+ end
+ end
+
+ def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
+ json(conn, %{})
+ end
+ end
+
def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> json("Not found")
end
+ def errors(conn, {:error, reason}) do
+ conn
+ |> put_status(400)
+ |> json(reason)
+ end
+
def errors(conn, {:param_cast, _}) do
conn
|> put_status(400)
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
new file mode 100644
index 000000000..47a73dc7e
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.ReportView do
+ use Pleroma.Web, :view
+ alias Pleroma.Activity
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI.Utils
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.MastodonAPI.StatusView
+
+ def render("index.json", %{reports: reports}) do
+ %{
+ reports: render_many(reports, __MODULE__, "show.json", as: :report)
+ }
+ end
+
+ def render("show.json", %{report: report}) do
+ user = User.get_cached_by_ap_id(report.data["actor"])
+ created_at = Utils.to_masto_date(report.data["published"])
+
+ [account_ap_id | status_ap_ids] = report.data["object"]
+ account = User.get_cached_by_ap_id(account_ap_id)
+
+ statuses =
+ Enum.map(status_ap_ids, fn ap_id ->
+ Activity.get_by_ap_id_with_object(ap_id)
+ end)
+
+ %{
+ id: report.id,
+ account: AccountView.render("account.json", %{user: account}),
+ actor: AccountView.render("account.json", %{user: user}),
+ content: report.data["content"],
+ created_at: created_at,
+ statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
+ state: report.data["state"]
+ }
+ end
+end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 29c4c1014..208c12c7b 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -71,6 +71,9 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, _} <- unpin(activity_id, user),
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
+ else
+ _ ->
+ {:error, "Could not delete"}
end
end
@@ -315,6 +318,60 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def update_report_state(activity_id, state) do
+ with %Activity{} = activity <- Activity.get_by_id(activity_id),
+ {:ok, activity} <- Utils.update_report_state(activity, state) do
+ {:ok, activity}
+ else
+ nil ->
+ {:error, :not_found}
+
+ {:error, reason} ->
+ {:error, reason}
+
+ _ ->
+ {:error, "Could not update state"}
+ end
+ end
+
+ def update_activity_scope(activity_id, opts \\ %{}) do
+ with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
+ {:ok, activity} <- toggle_sensitive(activity, opts),
+ {:ok, activity} <- set_visibility(activity, opts) do
+ {:ok, activity}
+ else
+ nil ->
+ {:error, :not_found}
+
+ {:error, reason} ->
+ {:error, reason}
+ end
+ end
+
+ defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do
+ toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)})
+ end
+
+ defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive})
+ when is_boolean(sensitive) do
+ new_data = Map.put(object.data, "sensitive", sensitive)
+
+ {:ok, object} =
+ object
+ |> Object.change(%{data: new_data})
+ |> Object.update_and_set_cache()
+
+ {:ok, Map.put(activity, :object, object)}
+ end
+
+ defp toggle_sensitive(activity, _), do: {:ok, activity}
+
+ defp set_visibility(activity, %{"visibility" => visibility}) do
+ Utils.update_activity_visibility(activity, visibility)
+ end
+
+ defp set_visibility(activity, _), do: {:ok, activity}
+
def hide_reblogs(user, muted) do
ap_id = muted.ap_id
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 1dfe50b40..bee2fd159 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -237,13 +237,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}
- if in_reply_to do
- in_reply_to_object = Object.normalize(in_reply_to)
-
- object
- |> Map.put("inReplyTo", in_reply_to_object.data["id"])
+ with false <- is_nil(in_reply_to),
+ %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
+ Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
else
- object
+ _ -> object
end
end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 87e597074..66056a846 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -303,7 +303,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
activities =
[user.ap_id | user.following]
|> ActivityPub.fetch_activities(params)
- |> ActivityPub.contain_timeline(user)
|> Enum.reverse()
conn
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 7fef82f82..6a4e4a1d4 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -194,6 +194,14 @@ defmodule Pleroma.Web.Router do
get("/users", AdminAPIController, :list_users)
get("/users/:nickname", AdminAPIController, :user_show)
+
+ get("/reports", AdminAPIController, :list_reports)
+ get("/reports/:id", AdminAPIController, :report_show)
+ put("/reports/:id", AdminAPIController, :report_update_state)
+ post("/reports/:id/respond", AdminAPIController, :report_respond)
+
+ put("/statuses/:id", AdminAPIController, :status_update)
+ delete("/statuses/:id", AdminAPIController, :status_delete)
end
scope "/", Pleroma.Web.TwitterAPI do
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 3c5a70be9..31e86685a 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -101,9 +101,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> Map.put("blocking_user", user)
|> Map.put("user", user)
- activities =
- ActivityPub.fetch_activities([user.ap_id | user.following], params)
- |> ActivityPub.contain_timeline(user)
+ activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
conn
|> put_view(ActivityView)