Introduce group basic federation, event new page and notifications

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-02-18 08:57:00 +01:00
parent 300ef8f245
commit 4144e9ffd0
416 changed files with 32220 additions and 16750 deletions

View File

@@ -9,8 +9,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do
alias Mobilizon.Actors.Actor
alias Mobilizon.Admin.{ActionLog, Setting}
alias Mobilizon.Config
alias Mobilizon.Events.{Comment, Event}
alias Mobilizon.Events.{Comment, Event}
alias Mobilizon.Conversations.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Reports.{Note, Report}
alias Mobilizon.Service.Statistics

View File

@@ -3,10 +3,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
Handles the comment-related GraphQL calls.
"""
alias Mobilizon.{Actors, Admin, Events}
alias Mobilizon.{Actors, Admin, Conversations}
alias Mobilizon.Actors.Actor
alias Mobilizon.Events
alias Mobilizon.Events.Comment, as: CommentModel
alias Mobilizon.Conversations.Comment, as: CommentModel
alias Mobilizon.Users
alias Mobilizon.Users.User
alias Mobilizon.GraphQL.API.Comments
@@ -14,13 +14,17 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
require Logger
def get_thread(_parent, %{id: thread_id}, _context) do
{:ok, Events.get_thread_replies(thread_id)}
{:ok, Conversations.get_thread_replies(thread_id)}
end
def create_comment(
_parent,
%{actor_id: actor_id} = args,
%{context: %{current_user: %User{} = user}}
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:is_owned, %Actor{} = _organizer_actor} <- User.owns_actor(user, actor_id),
{:ok, _, %CommentModel{} = comment} <-
@@ -36,14 +40,40 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
{:error, "You are not allowed to create a comment if not connected"}
end
def update_comment(
_parent,
%{text: text, comment_id: comment_id},
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
%CommentModel{actor_id: comment_actor_id} = comment <-
Mobilizon.Conversations.get_comment(comment_id),
true <- actor_id === comment_actor_id,
{:ok, _, %CommentModel{} = comment} <- Comments.update_comment(comment, %{text: text}) do
{:ok, comment}
end
end
def edit_comment(_parent, _args, _context) do
{:error, "You are not allowed to update a comment if not connected"}
end
def delete_comment(
_parent,
%{actor_id: actor_id, comment_id: comment_id},
%{context: %{current_user: %User{role: role} = user}}
%{
context: %{
current_user: %User{role: role} = user
}
}
) do
with {actor_id, ""} <- Integer.parse(actor_id),
{:is_owned, %Actor{} = _organizer_actor} <- User.owns_actor(user, actor_id),
%CommentModel{} = comment <- Events.get_comment_with_preload(comment_id) do
%CommentModel{} = comment <- Conversations.get_comment_with_preload(comment_id) do
cond do
{:comment_can_be_managed, true} == CommentModel.can_be_managed_by(comment, actor_id) ->
do_delete_comment(comment)

View File

@@ -100,7 +100,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
endpoint: Config.instance_maps_tiles_endpoint(),
attribution: Config.instance_maps_tiles_attribution()
}
}
},
resource_providers: Config.instance_resource_providers(),
timezones: Tzdata.zone_list()
}
end
end

View File

@@ -0,0 +1,110 @@
defmodule Mobilizon.GraphQL.Resolvers.Conversation do
@moduledoc """
Handles the group-related GraphQL calls.
"""
alias Mobilizon.{Actors, Conversations, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations.Conversation, as: ConversationModel
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
def find_conversations_for_actor(
%Actor{id: group_id},
_args,
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do
{:ok, Conversations.find_conversations_for_actor(group_id)}
else
{:member, false} ->
{:ok, %Page{total: 0, elements: []}}
end
end
def find_conversations_for_actor(%Actor{}, _args, _resolution) do
{:ok, %Page{total: 0, elements: []}}
end
def get_conversation(_parent, %{id: id}, _resolution) do
{:ok, Conversations.get_conversation(id)}
end
def get_comments_for_conversation(
%ConversationModel{id: conversation_id},
%{page: page, limit: limit},
_resolution
) do
{:ok, Conversations.get_comments_for_conversation(conversation_id, page, limit)}
end
def create_conversation(
_parent,
%{title: title, text: text, actor_id: actor_id, creator_id: creator_id},
_resolution
) do
with {:ok, %ConversationModel{} = conversation} <-
Conversations.create_conversation(%{
title: title,
text: text,
actor_id: actor_id,
creator_id: creator_id
}) do
{:ok, conversation}
end
end
def reply_to_conversation(
_parent,
%{text: text, conversation_id: conversation_id},
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:no_conversation, %ConversationModel{} = conversation} <-
{:no_conversation, Conversations.get_conversation(conversation_id)},
{:ok, %ConversationModel{} = conversation} <-
Conversations.reply_to_conversation(
conversation,
%{
text: text,
actor_id: actor_id
}
) do
{:ok, conversation}
end
end
@spec update_conversation(map(), map(), map()) :: {:ok, ConversationModel.t()}
def update_conversation(
_parent,
%{title: title, conversation_id: conversation_id},
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:no_conversation, %ConversationModel{creator_id: creator_id} = conversation} <-
{:no_conversation, Conversations.get_conversation(conversation_id)},
{:check_access, true} <- {:check_access, actor_id == creator_id},
{:ok, %ConversationModel{} = conversation} <-
Conversations.update_conversation(
conversation,
%{
title: title
}
) do
{:ok, conversation}
end
end
end

View File

@@ -3,23 +3,50 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
Handles the group-related GraphQL calls.
"""
alias Mobilizon.Actors
alias Mobilizon.{Actors, Events, Users}
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Users.User
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.GraphQL.API
alias Mobilizon.GraphQL.Resolvers.Person
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
require Logger
@doc """
Find a group
"""
def find_group(
parent,
%{preferred_username: name} = args,
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:ok, %Actor{id: group_id} = group} <-
ActivityPub.find_or_make_group_from_nickname(name),
{:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
group <- Person.proxify_pictures(group) do
{:ok, group}
else
{:member, false} ->
find_group(parent, args, nil)
_ ->
{:error, "Group with name #{name} not found"}
end
end
@doc """
Find a group
"""
def find_group(_parent, %{preferred_username: name}, _resolution) do
with {:ok, actor} <- ActivityPub.find_or_make_group_from_nickname(name),
actor <- Person.proxify_pictures(actor) do
%Actor{} = actor <- Person.proxify_pictures(actor),
%Actor{} = actor <- restrict_fields_for_non_member_request(actor) do
{:ok, actor}
else
_ ->
@@ -31,18 +58,21 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
Lists all groups
"""
def list_groups(_parent, %{page: page, limit: limit}, _resolution) do
{
:ok,
page
|> Actors.list_groups(limit)
|> Enum.map(fn actor -> Person.proxify_pictures(actor) end)
}
{:ok, Actors.list_groups(page, limit)}
end
@doc """
Create a new group. The creator is automatically added as admin
"""
def create_group(_parent, args, %{context: %{current_user: user}}) do
def create_group(
_parent,
args,
%{
context: %{
current_user: user
}
}
) do
with creator_actor_id <- Map.get(args, :creator_actor_id),
{:is_owned, %Actor{} = creator_actor} <- User.owns_actor(user, creator_actor_id),
args <- Map.put(args, :creator_actor, creator_actor),
@@ -68,14 +98,18 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
def delete_group(
_parent,
%{group_id: group_id, actor_id: actor_id},
%{context: %{current_user: user}}
%{
context: %{
current_user: user
}
}
) do
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, %Actor{}} <- User.owns_actor(user, actor_id),
{:ok, %Member{} = member} <- Actors.get_member(actor_id, group.id),
{:is_admin, true} <- Member.is_administrator(member),
{:is_admin, true} <- {:is_admin, Member.is_administrator(member)},
group <- Actors.delete_group!(group) do
{:ok, %{id: group.id}}
else
@@ -103,7 +137,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
def join_group(
_parent,
%{group_id: group_id, actor_id: actor_id},
%{context: %{current_user: user}}
%{
context: %{
current_user: user
}
}
) do
with {actor_id, ""} <- Integer.parse(actor_id),
{group_id, ""} <- Integer.parse(group_id),
@@ -146,7 +184,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
def leave_group(
_parent,
%{group_id: group_id, actor_id: actor_id},
%{context: %{current_user: user}}
%{
context: %{
current_user: user
}
}
) do
with {actor_id, ""} <- Integer.parse(actor_id),
{group_id, ""} <- Integer.parse(group_id),
@@ -156,7 +198,17 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
{:only_administrator, check_that_member_is_not_last_administrator(group_id, actor_id)},
{:ok, _} <-
Mobilizon.Actors.delete_member(member) do
{:ok, %{parent: %{id: group_id}, actor: %{id: actor_id}}}
{
:ok,
%{
parent: %{
id: group_id
},
actor: %{
id: actor_id
}
}
}
else
{:is_owned, nil} ->
{:error, "Actor id is not owned by authenticated user"}
@@ -173,17 +225,65 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
{:error, "You need to be logged-in to leave a group"}
end
def find_events_for_group(
%Actor{id: group_id} = group,
_args,
%{
context: %{
current_user: %User{} = user
}
}
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do
# TODO : Handle public / restricted to group members events
{:ok, Events.list_organized_events_for_group(group)}
else
{:member, false} ->
{:ok, %Page{total: 0, elements: []}}
end
end
def find_events_for_group(_parent, _args, _resolution) do
{:ok, %Page{total: 0, elements: []}}
end
# We check that the actor asking to leave the group is not it's only administrator
# We start by fetching the list of administrator or creators and if there's only one of them
# and that it's the actor requesting leaving the group we return true
@spec check_that_member_is_not_last_administrator(integer, integer) :: boolean
defp check_that_member_is_not_last_administrator(group_id, actor_id) do
case Actors.list_administrator_members_for_group(group_id) do
[%Member{actor: %Actor{id: member_actor_id}}] ->
%Page{total: total} when total > 1 ->
true
%Page{
total: 1,
elements: [
%Member{
actor: %Actor{
id: member_actor_id
}
}
]
} ->
actor_id == member_actor_id
_ ->
false
end
end
defp restrict_fields_for_non_member_request(%Actor{} = group) do
Map.merge(
group,
%{
followers: [],
followings: [],
organized_events: [],
comments: [],
feed_tokens: []
}
)
end
end

View File

@@ -3,14 +3,92 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
Handles the member-related GraphQL calls
"""
alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor}
alias Mobilizon.{Actors, Users}
alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
@doc """
Find members for group
Find members for group.
If actor requesting is not part of the group, we only return the number of members, not members
"""
def find_members_for_group(%Actor{} = actor, _args, _resolution) do
members = Actors.list_members_for_group(actor)
{:ok, members}
def find_members_for_group(
%Actor{id: group_id} = group,
_args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
%Page{} = page <- Actors.list_members_for_group(group) do
{:ok, page}
else
{:member, false} ->
# Actor is not member of group, fallback to public
with %Page{} = page <- Actors.list_members_for_group(group) do
{:ok, %Page{page | elements: []}}
end
end
end
def find_members_for_group(%Actor{} = group, _args, _resolution) do
with %Page{} = page <- Actors.list_members_for_group(group) do
{:ok, %Page{page | elements: []}}
end
end
def invite_member(
_parent,
%{group_id: group_id, target_actor_username: target_actor_username},
%{context: %{current_user: %User{} = user}}
) do
with %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user),
{:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id),
{:has_rights_to_invite, {:ok, %Member{role: role}}}
when role in [:moderator, :administrator, :creator] <-
{:has_rights_to_invite, Actors.get_member(actor_id, group_id)},
{:target_actor_username, {:ok, %Actor{id: target_actor_id} = target_actor}} <-
{:target_actor_username,
ActivityPub.find_or_make_actor_from_nickname(target_actor_username)},
{:error, :member_not_found} <- Actors.get_member(target_actor_id, group.id),
{:ok, _activity, %Member{} = member} <- ActivityPub.invite(group, actor, target_actor) do
{:ok, member}
else
{:is_owned, nil} ->
{:error, "Actor id is not owned by authenticated user"}
{:error, :group_not_found} ->
{:error, "Group id not found"}
{:target_actor_username, _} ->
{:error, "Actor invited doesn't exist"}
{:has_rights_to_invite, {:error, :member_not_found}} ->
{:error, "You are not a member of this group"}
{:has_rights_to_invite, _} ->
{:error, "You cannot invite to this group"}
{:ok, %Member{}} ->
{:error, "Actor is already a member of this group"}
end
end
def accept_invitation(_parent, %{id: member_id}, %{context: %{current_user: %User{} = user}}) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
%Member{actor: %Actor{id: member_actor_id}} = member <- Actors.get_member(member_id),
{:is_same_actor, true} <- {:is_same_actor, member_actor_id === actor_id},
{:ok, _activity, %Member{} = member} <-
ActivityPub.accept(
:invite,
member,
true
) do
# Launch an async task to refresh the group profile, fetch resources, discussions, members
{:ok, member}
end
end
end

View File

@@ -105,7 +105,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
@spec do_actor_join_event(Actor.t(), integer | String.t(), map()) ::
{:ok, Participant.t()} | {:error, String.t()}
defp do_actor_join_event(actor, event_id, args \\ %{}) do
defp do_actor_join_event(actor, event_id, args) do
with {:has_event, {:ok, %Event{} = event}} <-
{:has_event, Events.get_event_with_preload(event_id)},
{:ok, _activity, participant} <- Participations.join(event, actor, args),

View File

@@ -224,6 +224,19 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
end
end
@doc """
Returns the list of events this person is going to
"""
def person_memberships(%Actor{id: actor_id}, _args, %{context: %{current_user: user}}) do
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
participations <- Actors.list_members_for_actor(actor) do
{:ok, participations}
else
{:is_owned, nil} ->
{:error, "Actor id is not owned by authenticated user"}
end
end
def proxify_pictures(%Actor{} = actor) do
actor
|> proxify_avatar

View File

@@ -0,0 +1,230 @@
defmodule Mobilizon.GraphQL.Resolvers.Resource do
@moduledoc """
Handles the resources-related GraphQL calls
"""
alias Mobilizon.{Actors, Resources, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Resources.Resource
alias Mobilizon.Resources.Resource.Metadata
alias Mobilizon.Service.RichMedia.Parser
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
require Logger
@doc """
Find resources for group.
Returns only if actor requesting is a member of the group
"""
def find_resources_for_group(
%Actor{id: group_id} = group,
%{page: page, limit: limit},
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
%Page{} = page <- Resources.get_resources_for_group(group, page, limit) do
{:ok, page}
else
{:member, _} ->
find_resources_for_group(nil, nil, nil)
end
end
def find_resources_for_group(
_group,
_args,
_resolution
) do
{:ok, %Page{total: 0, elements: []}}
end
def find_resources_for_parent(
%Resource{actor_id: group_id} = parent,
_args,
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
%Page{} = page <- Resources.get_resources_for_folder(parent) do
{:ok, page}
end
end
def find_resources_for_parent(_parent, _args, _resolution),
do: {:ok, %Page{total: 0, elements: []}}
def get_resource(
_parent,
%{path: path, username: username},
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with {:current_actor, %Actor{id: actor_id}} <-
{:current_actor, Users.get_actor_for_user(user)},
{:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(username, :Group)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:resource, %Resource{} = resource} <-
{:resource, Resources.get_resource_by_group_and_path_with_preloads(group_id, path)} do
{:ok, resource}
else
{:member, false} -> {:error, "Actor is not member of group"}
{:resource, _} -> {:error, "No such resource"}
end
end
def get_resource(_parent, _args, _resolution) do
{:error, "You need to be logged-in to access resources"}
end
def create_resource(
_parent,
%{actor_id: group_id} = args,
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
parent <- get_eventual_parent(args),
{:own_check, true} <- {:own_check, check_resource_owned_by_group(parent, group_id)},
{:ok, _, %Resource{} = resource} <-
ActivityPub.create(
:resource,
args
|> Map.put(:actor_id, group_id)
|> Map.put(:creator_id, actor_id),
true,
%{}
) do
{:ok, resource}
else
{:own_check, _} ->
{:error, "Parent resource doesn't match this group"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def create_resource(_parent, _args, _resolution) do
{:error, "You need to be logged-in to create resources"}
end
def update_resource(
_parent,
%{id: resource_id} = args,
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:resource, %Resource{actor_id: group_id} = resource} <-
{:resource, Resources.get_resource_with_preloads(resource_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %Resource{} = resource} <-
ActivityPub.update(:resource, resource, args, true, %{}) do
{:ok, resource}
else
{:resource, _} ->
{:error, "Resource doesn't exist"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def update_resource(_parent, _args, _resolution) do
{:error, "You need to be logged-in to update resources"}
end
def delete_resource(
_parent,
%{id: resource_id},
%{
context: %{
current_user: %User{} = user
}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:resource, %Resource{parent_id: _parent_id, actor_id: group_id} = resource} <-
{:resource, Resources.get_resource_with_preloads(resource_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %Resource{} = resource} <-
ActivityPub.delete(resource) do
{:ok, resource}
else
{:resource, _} ->
{:error, "Resource doesn't exist"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def delete_resource(_parent, _args, _resolution) do
{:error, "You need to be logged-in to delete resources"}
end
def preview_resource_link(
_parent,
%{resource_url: resource_url},
%{
context: %{
current_user: %User{} = _user
}
} = _resolution
) do
with {:ok, data} when is_map(data) <- Parser.parse(resource_url) do
{:ok, struct(Metadata, data)}
end
end
def preview_resource_link(_parent, _args, _resolution) do
{:error, "You need to be logged-in to view a resource preview"}
end
@spec get_eventual_parent(map()) :: Resource.t() | nil
defp get_eventual_parent(args) do
parent = args |> Map.get(:parent_id) |> get_parent_resource()
case parent do
%Resource{} -> parent
_ -> nil
end
end
@spec get_parent_resource(integer | nil) :: nil | Resource.t()
defp get_parent_resource(nil), do: nil
defp get_parent_resource(parent_id), do: Resources.get_resource(parent_id)
@spec check_resource_owned_by_group(Resource.t() | nil, integer) :: boolean
defp check_resource_owned_by_group(nil, _group_id), do: true
defp check_resource_owned_by_group(%Resource{actor_id: actor_id}, group_id)
when is_binary(group_id),
do: actor_id == String.to_integer(group_id)
defp check_resource_owned_by_group(%Resource{actor_id: actor_id}, group_id)
when is_number(group_id),
do: actor_id == group_id
end

View File

@@ -0,0 +1,255 @@
defmodule Mobilizon.GraphQL.Resolvers.Todos do
@moduledoc """
Handles the todos related GraphQL calls
"""
alias Mobilizon.{Actors, Todos, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Storage.Page
alias Mobilizon.Todos.{Todo, TodoList}
alias Mobilizon.Users.User
require Logger
@doc """
Find todo lists for group.
Returns only if actor requesting is a member of the group
"""
def find_todo_lists_for_group(
%Actor{id: group_id} = group,
_args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
%Page{} = page <- Todos.get_todo_lists_for_group(group) do
{:ok, page}
else
{:member, _} ->
with %Page{} = page <- Todos.get_todo_lists_for_group(group) do
{:ok, %Page{page | elements: []}}
end
end
end
def find_todo_lists_for_group(_parent, _args, _resolution) do
{:ok, %Page{total: 0, elements: []}}
end
def find_todos_for_todo_list(
%TodoList{actor_id: group_id} = todo_list,
_args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
%Page{} = page <- Todos.get_todos_for_todo_list(todo_list) do
{:ok, page}
else
{:is_owned, nil} ->
{:error, "Actor id is not owned by authenticated user"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def get_todo_list(
_parent,
%{id: todo_list_id},
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with {:actor, %Actor{id: actor_id}} <- {:actor, Users.get_actor_for_user(user)},
{:todo, %TodoList{actor_id: group_id} = todo} <-
{:todo, Todos.get_todo_list(todo_list_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do
{:ok, todo}
else
{:todo, nil} ->
{:error, "Todo list doesn't exist"}
{:actor, nil} ->
{:error, "No actor found for user"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def create_todo_list(
_parent,
%{group_id: group_id} = args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %TodoList{} = todo_list} <-
ActivityPub.create(:todo_list, Map.put(args, :actor_id, group_id), true, %{}) do
{:ok, todo_list}
else
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
# def update_todo_list(
# _parent,
# %{id: todo_list_id, actor_id: actor_id},
# %{
# context: %{current_user: %User{} = user}
# } = _resolution
# ) do
# with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
# {:todo_list, %TodoList{actor_id: group_id} = todo_list} <-
# {:todo_list, Todos.get_todo_list(todo_list_id)},
# {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
# {:ok, _, %TodoList{} = todo} <-
# ActivityPub.update_todo_list(todo_list, actor, true, %{}) do
# {:ok, todo}
# else
# {:todo_list, _} ->
# {:error, "TodoList doesn't exist"}
# {:member, _} ->
# {:error, "Actor id is not member of group"}
# end
# end
# def delete_todo_list(
# _parent,
# %{id: todo_list_id, actor_id: actor_id},
# %{
# context: %{current_user: %User{} = user}
# } = _resolution
# ) do
# with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
# {:todo_list, %TodoList{actor_id: group_id} = todo_list} <-
# {:todo_list, Todos.get_todo_list(todo_list_id)},
# {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
# {:ok, _, %TodoList{} = todo} <-
# ActivityPub.delete_todo_list(todo_list, actor, true, %{}) do
# {:ok, todo}
# else
# {:todo_list, _} ->
# {:error, "TodoList doesn't exist"}
# {:member, _} ->
# {:error, "Actor id is not member of group"}
# end
# end
def get_todo(
_parent,
%{id: todo_id},
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with {:actor, %Actor{id: actor_id}} <- {:actor, Users.get_actor_for_user(user)},
{:todo, %Todo{todo_list_id: todo_list_id} = todo} <-
{:todo, Todos.get_todo(todo_id)},
{:todo_list, %TodoList{actor_id: group_id}} <-
{:todo_list, Todos.get_todo_list(todo_list_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do
{:ok, todo}
else
{:todo, nil} ->
{:error, "Todo doesn't exist"}
{:actor, nil} ->
{:error, "No actor found for user"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def create_todo(
_parent,
%{todo_list_id: todo_list_id} = args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:todo_list, %TodoList{actor_id: group_id} = _todo_list} <-
{:todo_list, Todos.get_todo_list(todo_list_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %Todo{} = todo} <-
ActivityPub.create(:todo, Map.put(args, :creator_id, actor_id), true, %{}) do
{:ok, todo}
else
{:todo_list, _} ->
{:error, "TodoList doesn't exist"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
def update_todo(
_parent,
%{id: todo_id} = args,
%{
context: %{current_user: %User{} = user}
} = _resolution
) do
with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:todo, %Todo{todo_list_id: todo_list_id} = todo} <-
{:todo, Todos.get_todo(todo_id)},
{:todo_list, %TodoList{actor_id: group_id}} <-
{:todo_list, Todos.get_todo_list(todo_list_id)},
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %Todo{} = todo} <-
ActivityPub.update(:todo, todo, args, true, %{}) do
{:ok, todo}
else
{:todo_list, _} ->
{:error, "TodoList doesn't exist"}
{:todo, _} ->
{:error, "Todo doesn't exist"}
{:member, _} ->
{:error, "Actor id is not member of group"}
end
end
# def delete_todo(
# _parent,
# %{id: todo_id, actor_id: actor_id},
# %{
# context: %{current_user: %User{} = user}
# } = _resolution
# ) do
# with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id),
# {:todo, %Todo{todo_list_id: todo_list_id} = todo} <-
# {:todo, Todos.get_todo(todo_id)},
# {:todo_list, %TodoList{actor_id: group_id}} <-
# {:todo_list, Todos.get_todo_list(todo_list_id)},
# {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
# {:ok, _, %Todo{} = todo} <-
# ActivityPub.delete_todo(todo, actor, true, %{}) do
# {:ok, todo}
# else
# {:todo_list, _} ->
# {:error, "TodoList doesn't exist"}
# {:todo, _} ->
# {:error, "Todo doesn't exist"}
# {:member, _} ->
# {:error, "Actor id is not member of group"}
# end
# end
end

View File

@@ -8,8 +8,8 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
alias Mobilizon.{Actors, Config, Events, Users}
alias Mobilizon.Actors.Actor
alias Mobilizon.Crypto
alias Mobilizon.Storage.Repo
alias Mobilizon.Users.User
alias Mobilizon.Storage.{Page, Repo}
alias Mobilizon.Users.{Setting, User}
alias Mobilizon.Web.{Auth, Email}
@@ -245,7 +245,7 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
%{context: %{current_user: %User{id: logged_user_id}}}
) do
with true <- user_id == logged_user_id,
participations <-
%Page{} = page <-
Events.list_participations_for_user(
user_id,
Map.get(args, :after_datetime),
@@ -253,7 +253,26 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
Map.get(args, :page),
Map.get(args, :limit)
) do
{:ok, participations}
{:ok, page}
end
end
@doc """
Returns the list of groups this user is a member is a member of
"""
def user_memberships(
%User{id: user_id},
%{page: page, limit: limit} = _args,
%{context: %{current_user: %User{id: logged_user_id}}}
) do
with true <- user_id == logged_user_id,
memberships <-
Actors.list_memberships_for_user(
user_id,
page,
limit
) do
{:ok, memberships}
end
end
@@ -379,4 +398,42 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
def delete_account(_parent, _args, _resolution) do
{:error, "You need to be logged-in to delete your account"}
end
@spec user_settings(User.t(), map(), map()) :: {:ok, list(Setting.t())} | {:error, String.t()}
def user_settings(%User{id: user_id} = user, _args, %{
context: %{current_user: %User{id: logged_user_id}}
}) do
with {:same_user, true} <- {:same_user, user_id == logged_user_id},
{:setting, settings} <- {:setting, Users.get_setting(user)} do
{:ok, settings}
else
{:same_user, _} ->
{:error, "User requested is not logged-in"}
end
end
@spec set_user_setting(map(), map(), map()) :: {:ok, Setting.t()} | {:error, any()}
def set_user_setting(_parent, attrs, %{
context: %{current_user: %User{id: logged_user_id}}
}) do
attrs = Map.put(attrs, :user_id, logged_user_id)
res =
case Users.get_setting(logged_user_id) do
nil ->
Users.create_setting(attrs)
%Setting{} = setting ->
Users.update_setting(setting, attrs)
end
case res do
{:ok, %Setting{} = setting} ->
{:ok, setting}
{:error, changeset} ->
Logger.debug(inspect(changeset))
{:error, "Error while saving user setting"}
end
end
end