Introduce group basic federation, event new page and notifications
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
110
lib/graphql/resolvers/conversation.ex
Normal file
110
lib/graphql/resolvers/conversation.ex
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
230
lib/graphql/resolvers/resource.ex
Normal file
230
lib/graphql/resolvers/resource.ex
Normal 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
|
||||
255
lib/graphql/resolvers/todos.ex
Normal file
255
lib/graphql/resolvers/todos.ex
Normal 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user