Add admin dashboard, event reporting, moderation report screens, moderation log

Close #156 and #158

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2019-09-09 09:31:08 +02:00
parent 164429964a
commit 27f2597b07
77 changed files with 1682 additions and 201 deletions

View File

@@ -2,10 +2,13 @@ defmodule MobilizonWeb.Resolvers.Admin do
@moduledoc """
Handles the report-related GraphQL calls
"""
alias Mobilizon.Events
alias Mobilizon.Users.User
import Mobilizon.Users.Guards
alias Mobilizon.Admin.ActionLog
alias Mobilizon.Reports.{Report, Note}
alias Mobilizon.Events.Event
alias Mobilizon.Service.Statistics
def list_action_logs(_parent, %{page: page, limit: limit}, %{
context: %{current_user: %User{role: role}}
@@ -17,14 +20,19 @@ defmodule MobilizonWeb.Resolvers.Admin do
target_type: target_type,
action: action,
actor: actor,
id: id
id: id,
inserted_at: inserted_at
} = action_log ->
transform_action_log(target_type, action, action_log)
|> Map.merge(%{
actor: actor,
id: id
})
with data when is_map(data) <-
transform_action_log(String.to_existing_atom(target_type), action, action_log) do
Map.merge(data, %{
actor: actor,
id: id,
inserted_at: inserted_at
})
end
end)
|> Enum.filter(& &1)
{:ok, action_logs}
end
@@ -35,38 +43,87 @@ defmodule MobilizonWeb.Resolvers.Admin do
end
defp transform_action_log(
"Elixir.Mobilizon.Reports.Report",
"update",
Report,
:update,
%ActionLog{} = action_log
) do
with %Report{status: status} = report <- Mobilizon.Reports.get_report(action_log.target_id) do
with %Report{} = report <- Mobilizon.Reports.get_report(action_log.target_id) do
action =
case action_log do
%ActionLog{changes: %{"status" => "closed"}} -> :report_update_closed
%ActionLog{changes: %{"status" => "open"}} -> :report_update_opened
%ActionLog{changes: %{"status" => "resolved"}} -> :report_update_resolved
end
%{
action: "report_update_" <> to_string(status),
action: action,
object: report
}
end
end
defp transform_action_log("Elixir.Mobilizon.Reports.Note", "create", %ActionLog{
defp transform_action_log(Note, :create, %ActionLog{
changes: changes
}) do
%{
action: "note_creation",
action: :note_creation,
object: convert_changes_to_struct(Note, changes)
}
end
defp transform_action_log("Elixir.Mobilizon.Reports.Note", "delete", %ActionLog{
defp transform_action_log(Note, :delete, %ActionLog{
changes: changes
}) do
%{
action: "note_deletion",
action: :note_deletion,
object: convert_changes_to_struct(Note, changes)
}
end
defp transform_action_log(Event, :delete, %ActionLog{
changes: changes
}) do
%{
action: :event_deletion,
object: convert_changes_to_struct(Event, changes)
}
end
# Changes are stored as %{"key" => "value"} so we need to convert them back as struct
defp convert_changes_to_struct(struct, %{"report_id" => _report_id} = changes) do
with data <- for({key, val} <- changes, into: %{}, do: {String.to_atom(key), val}),
data <- Map.put(data, :report, Mobilizon.Reports.get_report(data.report_id)) do
struct(struct, data)
end
end
defp convert_changes_to_struct(struct, changes) do
struct(struct, for({key, val} <- changes, into: %{}, do: {String.to_atom(key), val}))
with data <- for({key, val} <- changes, into: %{}, do: {String.to_atom(key), val}) do
struct(struct, data)
end
end
def get_dashboard(_parent, _args, %{
context: %{current_user: %User{role: role}}
})
when is_admin(role) do
last_public_event_published =
case Events.list_events(1, 1, :inserted_at, :desc) do
[event | _] -> event
_ -> nil
end
{:ok,
%{
number_of_users: Statistics.get_cached_value(:local_users),
number_of_events: Statistics.get_cached_value(:local_events),
number_of_comments: Statistics.get_cached_value(:local_comments),
number_of_reports: Mobilizon.Reports.count_opened_reports(),
last_public_event_published: last_public_event_published
}}
end
def get_dashboard(_parent, _args, _resolution) do
{:error, "You need to be logged-in and an administrator to access dashboard statistics"}
end
end

View File

@@ -9,7 +9,10 @@ defmodule MobilizonWeb.Resolvers.Event do
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Media.Picture
alias Mobilizon.Users.User
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor
alias MobilizonWeb.Resolvers.Person
import Mobilizon.Service.Admin.ActionLogService
# We limit the max number of events that can be retrieved
@event_max_limit 100
@@ -328,28 +331,43 @@ defmodule MobilizonWeb.Resolvers.Event do
%{event_id: event_id, actor_id: actor_id},
%{
context: %{
current_user: user
current_user: %User{role: role} = user
}
}
) do
with {:ok, %Event{} = event} <- Mobilizon.Events.get_event(event_id),
{:is_owned, true, _} <- User.owns_actor(user, actor_id),
{:event_can_be_managed, true} <- Event.can_event_be_managed_by(event, actor_id),
event <- Mobilizon.Events.delete_event!(event) do
{:ok, %{id: event.id}}
with {:ok, %Event{local: is_local} = event} <- Mobilizon.Events.get_event_full(event_id),
{actor_id, ""} <- Integer.parse(actor_id),
{:is_owned, true, _} <- User.owns_actor(user, actor_id) do
cond do
Event.can_event_be_managed_by(event, actor_id) == {:event_can_be_managed, true} ->
do_delete_event(event)
role in [:moderator, :administrator] ->
with {:ok, res} <- do_delete_event(event, !is_local),
%Actor{} = actor <- Actors.get_actor(actor_id) do
log_action(actor, "delete", event)
{:ok, res}
end
true ->
{:error, "You cannot delete this event"}
end
else
{:error, :event_not_found} ->
{:error, "Event not found"}
{:is_owned, false} ->
{:error, "Actor id is not owned by authenticated user"}
{:event_can_be_managed, false} ->
{:error, "You cannot delete this event"}
end
end
def delete_event(_parent, _args, _resolution) do
{:error, "You need to be logged-in to delete an event"}
end
defp do_delete_event(event, federate \\ true) when is_boolean(federate) do
with {:ok, _activity, event} <- MobilizonWeb.API.Events.delete_event(event) do
{:ok, %{id: event.id}}
end
end
end

View File

@@ -89,7 +89,9 @@ defmodule MobilizonWeb.Resolvers.Group do
}
}
) do
with {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id),
with {actor_id, ""} <- Integer.parse(actor_id),
{group_id, ""} <- Integer.parse(group_id),
{:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id),
{:is_owned, true, _} <- User.owns_actor(user, actor_id),
{:ok, %Member{} = member} <- Member.get_member(actor_id, group.id),
{:is_admin, true} <- Member.is_administrator(member),
@@ -126,7 +128,9 @@ defmodule MobilizonWeb.Resolvers.Group do
}
}
) do
with {:is_owned, true, actor} <- User.owns_actor(user, actor_id),
with {actor_id, ""} <- Integer.parse(actor_id),
{group_id, ""} <- Integer.parse(group_id),
{:is_owned, true, actor} <- User.owns_actor(user, actor_id),
{:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id),
{:error, :member_not_found} <- Member.get_member(actor.id, group.id),
{:is_able_to_join, true} <- {:is_able_to_join, Member.can_be_joined(group)},
@@ -180,7 +184,9 @@ defmodule MobilizonWeb.Resolvers.Group do
}
}
) do
with {:is_owned, true, actor} <- User.owns_actor(user, actor_id),
with {actor_id, ""} <- Integer.parse(actor_id),
{group_id, ""} <- Integer.parse(group_id),
{:is_owned, true, actor} <- User.owns_actor(user, actor_id),
{:ok, %Member{} = member} <- Member.get_member(actor.id, group_id),
{:only_administrator, false} <-
{:only_administrator, check_that_member_is_not_last_administrator(group_id, actor_id)},

View File

@@ -10,11 +10,11 @@ defmodule MobilizonWeb.Resolvers.Report do
alias MobilizonWeb.API.Reports, as: ReportsAPI
import Mobilizon.Users.Guards
def list_reports(_parent, %{page: page, limit: limit}, %{
def list_reports(_parent, %{page: page, limit: limit, status: status}, %{
context: %{current_user: %User{role: role}}
})
when is_moderator(role) do
{:ok, Mobilizon.Reports.list_reports(page, limit)}
{:ok, Mobilizon.Reports.list_reports(page, limit, :updated_at, :desc, status)}
end
def list_reports(_parent, _args, _resolution) do
@@ -25,7 +25,13 @@ defmodule MobilizonWeb.Resolvers.Report do
context: %{current_user: %User{role: role}}
})
when is_moderator(role) do
{:ok, Mobilizon.Reports.get_report(id)}
case Mobilizon.Reports.get_report(id) do
%Report{} = report ->
{:ok, report}
nil ->
{:error, "Report not found"}
end
end
def get_report(_parent, _args, _resolution) do