From f7d9f42243dabf992942ec4c8be1f1e07228634a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 27 Sep 2022 23:56:20 +0200 Subject: [PATCH] event editing? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../operations/pleroma_event_operation.ex | 61 +++++++++++++++++++ lib/pleroma/web/common_api.ex | 35 +++++++++++ lib/pleroma/web/common_api/activity_draft.ex | 8 ++- .../controllers/status_controller.ex | 26 +++----- .../controllers/event_controller.ex | 29 +++++++++ lib/pleroma/web/plugs/set_application_plug.ex | 28 +++++++++ lib/pleroma/web/router.ex | 1 + 7 files changed, 170 insertions(+), 18 deletions(-) create mode 100644 lib/pleroma/web/plugs/set_application_plug.ex diff --git a/lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex index 2cb25620c2..ed88e2ee16 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_event_operation.ex @@ -34,6 +34,23 @@ def create_operation do } end + def update_operation do + %Operation{ + tags: ["Event actions"], + summary: "Update event", + description: "Change the content of an event", + operationId: "PleromaAPI.EventController.update", + security: [%{"oAuth" => ["write"]}], + parameters: [id_param()], + requestBody: request_body("Parameters", update_request(), required: true), + responses: %{ + 200 => event_response(), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + def participations_operation do %Operation{ tags: ["Event actions"], @@ -217,6 +234,50 @@ defp create_request do } end + defp update_request do + %Schema{ + title: "EventUpdateRequest", + type: :object, + properties: %{ + name: %Schema{ + type: :string, + description: "Name of the event." + }, + status: %Schema{ + type: :string, + nullable: true, + description: "Text description of the event." + }, + banner_id: %Schema{ + nullable: true, + type: :string, + description: "Attachment id to be attached as banner." + }, + start_time: %Schema{ + type: :string, + format: :"date-time", + description: "Start time." + }, + end_time: %Schema{ + type: :string, + format: :"date-time", + description: "End time." + }, + location_id: %Schema{ + type: :string, + description: "Location ID from geospatial provider", + nullable: true + } + }, + example: %{ + "name" => "Updated event", + "status" => "We had to reschedule the event.", + "start_time" => "2022-02-22T22:00:00.000Z", + "end_time" => "2022-02-22T23:00:00.000Z" + } + } + end + defp event_response do Operation.response( "Status", diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index 544744734a..f9cb7998c3 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -743,4 +743,39 @@ def event(user, data, location \\ nil) do ActivityPub.create(draft.changes) end end + + def update_event(user, orig_activity, changes) do + with orig_object <- Object.normalize(orig_activity), + {:ok, new_object} <- make_update_event_data(user, orig_object, changes), + {:ok, update_data, _} <- Builder.update(user, new_object), + {:ok, update, _} <- Pipeline.common_pipeline(update_data, local: true) do + {:ok, update} + else + _ -> {:error, nil} + end + end + + defp make_update_event_data(user, orig_object, changes) do + kept_params = %{ + visibility: Visibility.get_visibility(orig_object), + in_reply_to_id: + with replied_id when is_binary(replied_id) <- orig_object.data["inReplyTo"], + %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(replied_id) do + activity_id + else + _ -> nil + end + } + + params = Map.merge(changes, kept_params) + + with {:ok, draft} <- ActivityDraft.event(user, params) do + change = + Object.Updater.make_update_object_data(orig_object.data, draft.object, Utils.make_date()) + + {:ok, change} + else + _ -> {:error, nil} + end + end end diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 1379f242e2..0d06cc81c5 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -135,7 +135,13 @@ defp event_object(draft) do "location_id" => draft.location_id, "location_provider" => draft.location_provider, "startTime" => draft.start_time, - "endTime" => draft.end_time + "endTime" => draft.end_time, + "source" => %{ + "content" => draft.status, + "mediaType" => Utils.get_content_type(draft.params[:content_type]) + }, + "generator" => draft.params[:generator], + "content_type" => draft.params[:content_type], } %__MODULE__{draft | object: object} diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 924e37aa7d..8246295e19 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -21,7 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView - alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.RateLimiter @@ -101,6 +100,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do plug(RateLimiter, [name: :statuses_actions] when action in @rate_limited_status_actions) + plug(Pleroma.Web.Plugs.SetApplicationPlug, [] when action in [:create, :update]) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.StatusOperation @@ -140,8 +141,9 @@ def create( ) when not is_nil(scheduled_at) do params = - Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) - |> put_application(conn) + params + |> Map.put(:in_reply_to_status_id, params[:in_reply_to_id]) + |> Map.put(:application, conn.assigns.application) attrs = %{ params: Map.new(params, fn {key, value} -> {to_string(key), value} end), @@ -166,8 +168,9 @@ def create( # Creates a regular status def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do params = - Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) - |> put_application(conn) + params + |> Map.put(:in_reply_to_status_id, params[:in_reply_to_id]) + |> Map.put(:application, conn.assigns.application) with {:ok, activity} <- CommonAPI.post(user, params) do try_render(conn, "show.json", @@ -231,7 +234,7 @@ def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} {_, true} <- {:is_create, activity.data["type"] == "Create"}, actor <- Activity.user_actor(activity), {_, true} <- {:own_status, actor.id == user.id}, - changes <- body_params |> put_application(conn), + changes <- body_params |> Map.put(:application, conn.assigns.application), {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(user, activity, changes)}, {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do try_render(conn, "show.json", @@ -484,15 +487,4 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do as: :activity ) end - - defp put_application(params, %{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do - if user.disclose_client do - %{client_name: client_name, website: website} = Repo.preload(token, :app).app - Map.put(params, :generator, %{type: "Application", name: client_name, url: website}) - else - Map.put(params, :generator, nil) - end - end - - defp put_application(params, _), do: Map.put(params, :generator, nil) end diff --git a/lib/pleroma/web/pleroma_api/controllers/event_controller.ex b/lib/pleroma/web/pleroma_api/controllers/event_controller.ex index 75bd9c62af..f9babb5702 100644 --- a/lib/pleroma/web/pleroma_api/controllers/event_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/event_controller.ex @@ -48,6 +48,7 @@ defmodule Pleroma.Web.PleromaAPI.EventController do %{scopes: ["write"]} when action in [ :create, + :update, :authorize_participation_request, :reject_participation_request, :join, @@ -67,6 +68,8 @@ defmodule Pleroma.Web.PleromaAPI.EventController do when action in [:export_ics] ) + plug(Pleroma.Web.Plugs.SetApplicationPlug, [] when action in [:create, :update]) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEventOperation def create(%{assigns: %{user: user}, body_params: params} = conn, _) do @@ -96,6 +99,32 @@ def create(%{assigns: %{user: user}, body_params: params} = conn, _) do end end + @doc "PUT /api/v1/statuses/:id" + def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do + with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)}, + {_, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, + {_, true} <- {:is_create, activity.data["type"] == "Create"}, + actor <- Activity.user_actor(activity), + {_, true} <- {:own_status, actor.id == user.id}, + changes <- body_params |> Map.put(:application, conn.assigns.application), + {_, {:ok, _update_activity}} <- + {:pipeline, CommonAPI.update_event(user, activity, changes)}, + {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do + conn + |> put_view(StatusView) + |> try_render("show.json", + activity: activity, + for: user, + with_direct_conversation_id: true, + with_muted: Map.get(params, :with_muted, false) + ) + else + {:own_status, _} -> {:error, :forbidden} + {:pipeline, e} -> {:error, :internal_server_error} + _ -> {:error, :not_found} + end + end + defp get_location(%{location_id: location_id}) when is_binary(location_id) do result = Geospatial.Service.service().get_by_id(location_id) diff --git a/lib/pleroma/web/plugs/set_application_plug.ex b/lib/pleroma/web/plugs/set_application_plug.ex new file mode 100644 index 0000000000..0eec3ae2b7 --- /dev/null +++ b/lib/pleroma/web/plugs/set_application_plug.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.SetApplicationPlug do + import Plug.Conn, only: [assign: 3] + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.OAuth.Token + + def init(_), do: nil + + def call(conn, _) do + assign(conn, :application, get_application(conn)) + end + + defp get_application(%{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do + if user.disclose_client do + %{client_name: client_name, website: website} = Repo.preload(token, :app).app + %{type: "Application", name: client_name, url: website} + else + nil + end + end + + defp get_application(_), do: nil +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index cbe4b371f1..be32e8ba55 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -488,6 +488,7 @@ defmodule Pleroma.Web.Router do post("/backups", BackupController, :create) post("/events", EventController, :create) + put("/events/:id", EventController, :update) get("/events/:id/participations", EventController, :participations) get("/events/:id/participation_requests", EventController, :participation_requests)