wip events
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
0c9e64265c
commit
3a6274f29a
17 changed files with 544 additions and 26 deletions
|
@ -19,7 +19,9 @@ defmodule Pleroma.Constants do
|
|||
"context_id",
|
||||
"deleted_activity_id",
|
||||
"pleroma_internal",
|
||||
"generator"
|
||||
"generator",
|
||||
"participants",
|
||||
"participant_count"
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -19,6 +19,30 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
def accept_or_reject(%User{ap_id: ap_id}, activity, type) do
|
||||
data = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"actor" => ap_id,
|
||||
"type" => type,
|
||||
"object" => activity.data["id"],
|
||||
"to" => [activity.actor]
|
||||
}
|
||||
|
||||
{:ok, data, []}
|
||||
end
|
||||
|
||||
def accept_or_reject(%Object{data: %{"actor" => actor}} = object, activity, type) do
|
||||
data = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"actor" => actor,
|
||||
"type" => type,
|
||||
"object" => activity.data["id"],
|
||||
"to" => [activity.actor]
|
||||
}
|
||||
|
||||
{:ok, data, []}
|
||||
end
|
||||
|
||||
def accept_or_reject(actor, activity, type) do
|
||||
data = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
|
@ -31,14 +55,14 @@ def accept_or_reject(actor, activity, type) do
|
|||
{:ok, data, []}
|
||||
end
|
||||
|
||||
@spec reject(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def reject(actor, rejected_activity) do
|
||||
accept_or_reject(actor, rejected_activity, "Reject")
|
||||
@spec reject(User.t() | Object.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def reject(object, rejected_activity) do
|
||||
accept_or_reject(object, rejected_activity, "Reject")
|
||||
end
|
||||
|
||||
@spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def accept(actor, accepted_activity) do
|
||||
accept_or_reject(actor, accepted_activity, "Accept")
|
||||
@spec accept(User.t() | Object.t(), Activity.t()) :: {:ok, map(), keyword()}
|
||||
def accept(object, accepted_activity) do
|
||||
accept_or_reject(object, accepted_activity, "Accept")
|
||||
end
|
||||
|
||||
@spec follow(User.t(), User.t()) :: {:ok, map(), keyword()}
|
||||
|
@ -337,4 +361,14 @@ def unpin(%User{} = user, object) do
|
|||
defp pinned_url(nickname) when is_binary(nickname) do
|
||||
Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)
|
||||
end
|
||||
|
||||
def join(actor, object) do
|
||||
with {:ok, data, meta} <- object_action(actor, object) do
|
||||
data =
|
||||
data
|
||||
|> Map.put("type", "Join")
|
||||
|
||||
{:ok, data, meta}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,6 +30,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.JoinValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
||||
|
@ -143,7 +144,7 @@ def validate(%{"type" => type} = object, meta)
|
|||
|
||||
def validate(%{"type" => type} = object, meta)
|
||||
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
|
||||
ChatMessage Answer] do
|
||||
ChatMessage Answer Join] do
|
||||
validator =
|
||||
case type do
|
||||
"Accept" -> AcceptRejectValidator
|
||||
|
@ -155,6 +156,7 @@ def validate(%{"type" => type} = object, meta)
|
|||
"Announce" -> AnnounceValidator
|
||||
"ChatMessage" -> ChatMessageValidator
|
||||
"Answer" -> AnswerValidator
|
||||
"Join" -> JoinValidator
|
||||
end
|
||||
|
||||
with {:ok, object} <-
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
@ -32,7 +33,7 @@ defp validate_data(cng) do
|
|||
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|
||||
|> validate_inclusion(:type, ["Accept", "Reject"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Follow"])
|
||||
|> validate_object_presence(allowed_types: ["Follow", "Join"])
|
||||
|> validate_accept_reject_rights()
|
||||
end
|
||||
|
||||
|
@ -44,8 +45,8 @@ def cast_and_validate(data) do
|
|||
|
||||
def validate_accept_reject_rights(cng) do
|
||||
with object_id when is_binary(object_id) <- get_field(cng, :object),
|
||||
%Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id),
|
||||
true <- followed_actor == get_field(cng, :actor) do
|
||||
activity <- Activity.get_by_ap_id(object_id),
|
||||
true <- validate_actor(activity, get_field(cng, :actor)) do
|
||||
cng
|
||||
else
|
||||
_e ->
|
||||
|
@ -53,4 +54,13 @@ def validate_accept_reject_rights(cng) do
|
|||
|> add_error(:actor, "can't accept or reject the given activity")
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Follow", "object" => followed_actor}}, actor) do
|
||||
followed_actor == actor
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Join", "object" => joined_event}}, actor) do
|
||||
%Object{data: %{"actor" => event_author}} = Object.get_cached_by_ap_id(joined_event)
|
||||
event_author == actor
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,6 +72,8 @@ defmacro event_object_fields do
|
|||
field(:startTime, ObjectValidators.DateTime)
|
||||
field(:endTime, ObjectValidators.DateTime)
|
||||
|
||||
field(:joinMode, :string, default: "free")
|
||||
|
||||
embeds_one(:location, PlaceValidator)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,6 +64,7 @@ def changeset(struct, data) do
|
|||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Event"])
|
||||
|> validate_inclusion(:joinMode, ~w[free restricted invite])
|
||||
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|
||||
|> CommonValidations.validate_any_presence([:cc, :to])
|
||||
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.JoinValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
quote do
|
||||
unquote do
|
||||
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
||||
message_fields()
|
||||
activity_fields()
|
||||
end
|
||||
end
|
||||
|
||||
field(:state, :string, default: "pending")
|
||||
|
||||
field(:participationMessage, :string)
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
data =
|
||||
data
|
||||
|> fix()
|
||||
|
||||
%__MODULE__{}
|
||||
|> changeset(data)
|
||||
end
|
||||
|
||||
def changeset(struct, data) do
|
||||
struct
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
||||
defp fix(data) do
|
||||
data =
|
||||
data
|
||||
|> CommonFixes.fix_actor()
|
||||
|> CommonFixes.fix_activity_addressing()
|
||||
|
||||
with %Object{} = object <- Object.normalize(data["object"]) do
|
||||
data
|
||||
|> CommonFixes.fix_activity_context(object)
|
||||
|> CommonFixes.fix_object_action_recipients(object)
|
||||
else
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_data(data_cng) do
|
||||
data_cng
|
||||
|> validate_inclusion(:type, ["Join"])
|
||||
|> validate_inclusion(:state, ~w{pending reject accept})
|
||||
|> validate_required([:id, :type, :object, :actor, :context, :to, :cc])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Event"])
|
||||
|> validate_existing_join()
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data()
|
||||
|> validate_data()
|
||||
end
|
||||
|
||||
defp validate_existing_join(%{changes: %{actor: actor, object: object}} = cng) do
|
||||
if Utils.get_existing_join(actor, %{data: %{"id" => object}}) do
|
||||
cng
|
||||
|> add_error(:actor, "already joined this event")
|
||||
|> add_error(:object, "already joined by this actor")
|
||||
else
|
||||
cng
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_existing_join(cng), do: cng
|
||||
end
|
|
@ -46,19 +46,14 @@ def handle(
|
|||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Accept",
|
||||
"object" => follow_activity_id
|
||||
"object" => activity_id
|
||||
}
|
||||
} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_accepted(activity, actor)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -74,21 +69,63 @@ def handle(
|
|||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Reject",
|
||||
"object" => follow_activity_id
|
||||
"object" => activity_id
|
||||
}
|
||||
} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
with %Activity{actor: follower_id} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_rejected(activity, actor)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
defp handle_accepted(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_accepted(
|
||||
%Activity{data: %{"type" => "Join", "object" => event_id}} = join_activity,
|
||||
_actor
|
||||
) do
|
||||
with joined_event <- Object.get_by_ap_id(event_id),
|
||||
{:o, join_activity} <- Utils.update_follow_state(join_activity, "accept") do
|
||||
Utils.add_participation_to_object(join_activity, joined_event)
|
||||
# Notification.update_notification_type(followed, follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_rejected(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do
|
||||
FollowingRelationship.update(follower, followed, :follow_reject)
|
||||
Notification.dismiss(follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
defp handle_rejected(
|
||||
%Activity{data: %{"type" => "Join", "object" => event_id}} = join_activity,
|
||||
_actor
|
||||
) do
|
||||
with joined_event <- Object.get_by_ap_id(event_id),
|
||||
{:o, join_activity} <- Utils.update_join_state(join_activity, "reject") do
|
||||
Utils.remove_participation_from_object(join_activity, joined_event)
|
||||
end
|
||||
end
|
||||
|
||||
# Tasks this handle
|
||||
|
@ -384,6 +421,20 @@ def handle(%{data: %{"type" => "Remove"} = data} = object, meta) do
|
|||
end
|
||||
end
|
||||
|
||||
# Tasks this handles:
|
||||
# accepts join if event is local
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Join"}} = object, meta) do
|
||||
joined_event = Object.get_by_ap_id(object.data["object"])
|
||||
|
||||
if Object.local?(joined_event) and joined_event.data["joinMode"] == "free" do
|
||||
{:ok, accept_data, _} = Builder.accept(joined_event, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
# Nothing to do
|
||||
@impl true
|
||||
def handle(object, meta) do
|
||||
|
|
|
@ -432,6 +432,29 @@ defp fetch_likes(object) do
|
|||
end
|
||||
end
|
||||
|
||||
def add_participation_to_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
[actor | fetch_participations(object)]
|
||||
|> Enum.uniq()
|
||||
|> update_participations_in_object(object)
|
||||
end
|
||||
|
||||
def remove_participation_from_object(%Activity{data: %{"actor" => actor}}, object) do
|
||||
List.delete(fetch_participations(object), actor)
|
||||
|> update_participations_in_object(object)
|
||||
end
|
||||
|
||||
defp update_participations_in_object(participations, object) do
|
||||
update_element_in_object("participation", participations, object)
|
||||
end
|
||||
|
||||
defp fetch_participations(object) do
|
||||
if is_list(object.data["participations"]) do
|
||||
object.data["participations"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
#### Follow-related helpers
|
||||
|
||||
@doc """
|
||||
|
@ -887,4 +910,26 @@ def get_existing_votes(actor, %{data: %{"id" => id}}) do
|
|||
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
### Join-related helpers
|
||||
def get_existing_join(actor, %{data: %{"id" => id}}) do
|
||||
actor
|
||||
|> Activity.Queries.by_actor()
|
||||
|> Activity.Queries.by_object_id(id)
|
||||
|> Activity.Queries.by_type("Join")
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def update_join_state(
|
||||
%Activity{} = activity,
|
||||
state
|
||||
) do
|
||||
new_data = Map.put(activity.data, "state", state)
|
||||
changeset = Changeset.change(activity, data: new_data)
|
||||
|
||||
with {:ok, activity} <- Repo.update(changeset) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
100
lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex
Normal file
100
lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaEventOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Publish new status",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Create a new event",
|
||||
operationId: "PleromaAPI.EventController.create",
|
||||
requestBody: request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
422 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def participate_operation do
|
||||
%Operation{
|
||||
tags: ["Event actions"],
|
||||
summary: "Participate",
|
||||
security: [%{"oAuth" => ["write"]}],
|
||||
description: "Participate in an event",
|
||||
operationId: "PleromaAPI.EventController.participate",
|
||||
parameters: [id_param()],
|
||||
responses: %{
|
||||
200 => event_response(),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_request do
|
||||
%Schema{
|
||||
title: "EventCreateRequest",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{
|
||||
type: :string,
|
||||
description: "Name of the event."
|
||||
},
|
||||
content: %Schema{
|
||||
type: :string,
|
||||
description: "Text description of the event."
|
||||
},
|
||||
start_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "Start time."
|
||||
},
|
||||
end_time: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
description: "End time."
|
||||
},
|
||||
join_mode: %Schema{
|
||||
type: :string,
|
||||
enum: ["free", "restricted"]
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"name" => "Example event",
|
||||
"content" => "No information for now.",
|
||||
"start_time" => "21-02-2022 22:00:00",
|
||||
"end_time" => "21-02-2022 23:00:00"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp event_response do
|
||||
Operation.response(
|
||||
"Status",
|
||||
"application/json",
|
||||
Status
|
||||
)
|
||||
end
|
||||
|
||||
defp id_param do
|
||||
Operation.parameter(:id, :path, FlakeID, "Event ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
end
|
|
@ -304,6 +304,43 @@ def vote(user, %{data: %{"type" => "Question"}} = object, choices) do
|
|||
end
|
||||
end
|
||||
|
||||
def join(%User{} = user, id) do
|
||||
case join_helper(user, id) do
|
||||
{:ok, _} = res ->
|
||||
res
|
||||
|
||||
{:error, :not_found} = res ->
|
||||
res
|
||||
|
||||
{:error, e} ->
|
||||
Logger.error("Could not join #{id}. Error: #{inspect(e, pretty: true)}")
|
||||
{:error, dgettext("errors", "Could not join")}
|
||||
end
|
||||
end
|
||||
|
||||
def join_helper(user, id) do
|
||||
with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
|
||||
{_, {:ok, join_object, meta}} <- {:build_object, Builder.join(user, object)},
|
||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
Pipeline.common_pipeline(join_object, Keyword.put(meta, :local, true))} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:find_object, _} ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:common_pipeline, {:error, {:validate, {:error, changeset}}}} = e ->
|
||||
if {:object, {"already joined by this actor", []}} in changeset.errors do
|
||||
{:ok, :already_joined}
|
||||
else
|
||||
{:error, e}
|
||||
end
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_not_author(%{data: %{"actor" => ap_id}}, %{ap_id: ap_id}),
|
||||
do: {:error, dgettext("errors", "Poll's author can't vote")}
|
||||
|
||||
|
@ -598,4 +635,10 @@ def get_user(ap_id, fake_record_fallback \\ true) do
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def event(user, data) do
|
||||
with {:ok, draft} <- ActivityDraft.event(user, data) do
|
||||
ActivityPub.create(draft.changes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,6 +88,32 @@ defp listen_object(draft) do
|
|||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
def event(user, params) do
|
||||
user
|
||||
|> new(params)
|
||||
|> visibility()
|
||||
|> to_and_cc()
|
||||
|> context()
|
||||
|> event_object()
|
||||
|> with_valid(&changes/1)
|
||||
|> validate()
|
||||
end
|
||||
|
||||
defp event_object(draft) do
|
||||
object =
|
||||
draft.params
|
||||
|> Map.take([:title, :content])
|
||||
|> Map.put("type", "Event")
|
||||
|> Map.put("to", draft.to)
|
||||
|> Map.put("cc", draft.cc)
|
||||
|> Map.put("actor", draft.user.ap_id)
|
||||
|> Map.put("startTime", draft.params[:start_time])
|
||||
|> Map.put("endTime", draft.params[:end_time])
|
||||
|> Map.put("joinMode", draft.params[:join_mode])
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
defp put_params(draft, params) do
|
||||
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
|
||||
%__MODULE__{draft | params: params}
|
||||
|
|
|
@ -562,6 +562,8 @@ def build_event(%{"type" => "Event"} = data) do
|
|||
%{
|
||||
start_time: data["startTime"],
|
||||
end_time: data["endTime"],
|
||||
join_mode: data["joinMode"],
|
||||
participants_count: data["participant_count"]
|
||||
}
|
||||
end
|
||||
|
||||
|
|
77
lib/pleroma/web/pleroma_api/controllers/event_controller.ex
Normal file
77
lib/pleroma/web/pleroma_api/controllers/event_controller.ex
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EventController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [try_render: 3]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
when action in [:create, :participate]
|
||||
)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEventOperation
|
||||
|
||||
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||
with {:ok, activity} <- CommonAPI.event(user, params) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json",
|
||||
activity: activity,
|
||||
for: user,
|
||||
as: :activity
|
||||
)
|
||||
else
|
||||
{:error, {:reject, message}} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
def participations(conn, %{"id" => activity_id}) do
|
||||
end
|
||||
|
||||
def participation_requests(conn, %{"id" => activity_id}) do
|
||||
%Activity{object: %Object{data: %{"id" => ap_id}}} = activity <-
|
||||
Activity.get_by_id_with_object(activity_id)
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.put(:type, "Join")
|
||||
|> Map.put(:object, ap_id)
|
||||
|> Map.put(:state, "pending")
|
||||
|
||||
activities =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
|
||||
end
|
||||
|
||||
def participate(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
|
||||
with {:ok, _} <- CommonAPI.join(user, activity_id),
|
||||
%Activity{} = activity <- Activity.get_by_id(activity_id) do
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> try_render("show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
end
|
24
lib/pleroma/web/pleroma_api/views/event_view.ex
Normal file
24
lib/pleroma/web/pleroma_api/views/event_view.ex
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.EventView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.MastodonAPI
|
||||
|
||||
def render(
|
||||
"participation_requests.json",
|
||||
%{participation_requests: participation_requests} = opts
|
||||
) do
|
||||
render_many(
|
||||
participation_requests,
|
||||
__MODULE__,
|
||||
"participation_request.json",
|
||||
Map.delete(opts, :participation_requests)
|
||||
)
|
||||
end
|
||||
|
||||
def render("participation_request.json", %{participation_request: participation_request} = opts) do
|
||||
%{}
|
||||
end
|
||||
end
|
|
@ -441,6 +441,9 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/backups", BackupController, :index)
|
||||
post("/backups", BackupController, :create)
|
||||
|
||||
post("/events", EventController, :create)
|
||||
post("/events/:id/participate", EventController, :participate)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
|
@ -36,7 +36,16 @@
|
|||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
},
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#"
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"mz": "https://joinmobilizon.org/ns#",
|
||||
"joinMode": {
|
||||
"@id": "mz:joinMode",
|
||||
"@type": "mz:joinModeType"
|
||||
},
|
||||
"joinModeType": {
|
||||
"@id": "mz:joinModeType",
|
||||
"@type": "rdfs:Class"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue