diff --git a/js/src/types/event.model.ts b/js/src/types/event.model.ts index d357c5d3a..2d34844c5 100644 --- a/js/src/types/event.model.ts +++ b/js/src/types/event.model.ts @@ -275,7 +275,7 @@ export class EventModel implements IEvent { this.title = hash.title; this.slug = hash.slug; - this.description = hash.description; + this.description = hash.description || ""; this.beginsOn = new Date(hash.beginsOn); if (hash.endsOn) this.endsOn = new Date(hash.endsOn); diff --git a/js/src/views/Event/Edit.vue b/js/src/views/Event/Edit.vue index 2ad2d6f80..06e38968a 100644 --- a/js/src/views/Event/Edit.vue +++ b/js/src/views/Event/Edit.vue @@ -1,6 +1,6 @@ <template> <section> - <div class="container"> + <div class="container" v-if="isCurrentActorOrganizer"> <h1 class="title" v-if="isUpdate === true"> {{ $t("Update event {name}", { name: event.title }) }} </h1> @@ -255,6 +255,7 @@ aria-label="main navigation" class="navbar" :class="{ 'is-fixed-bottom': showFixedNavbar }" + v-if="isCurrentActorOrganizer" > <div class="container"> <div class="navbar-menu"> @@ -511,6 +512,8 @@ export default class EditEvent extends Vue { this.limitedPlaces = this.event.options.maximumAttendeeCapacity > 0; if (!(this.isUpdate || this.isDuplicate)) { this.initializeEvent(); + } else { + this.event.description = this.event.description || ""; } } @@ -533,11 +536,6 @@ export default class EditEvent extends Vue { } } - @Watch("currentActor") - setCurrentActor(): void { - this.event.organizerActor = this.currentActor; - } - @Watch("event") setInitialData(): void { if (this.isUpdate && this.unmodifiedEvent === undefined && this.event && this.event.uuid) { @@ -620,6 +618,14 @@ export default class EditEvent extends Vue { } } + get isCurrentActorOrganizer(): boolean { + return !( + this.eventId && + this.event.organizerActor && + this.currentActor.id !== this.event.organizerActor.id + ) as boolean; + } + get updateEventMessage(): string { if (this.unmodifiedEvent.draft && !this.event.draft) return this.$i18n.t("The event has been updated and published") as string; @@ -720,6 +726,10 @@ export default class EditEvent extends Vue { * Build variables for Event GraphQL creation query */ private async buildVariables() { + this.event.organizerActor = + this.event.organizerActor && this.event.organizerActor.id + ? this.event.organizerActor + : this.currentActor; let res = this.event.toEditJSON(); if (this.event.organizerActor) { res = Object.assign(res, { diff --git a/lib/graphql/resolvers/event.ex b/lib/graphql/resolvers/event.ex index 16e5c3479..3be1cfdf5 100644 --- a/lib/graphql/resolvers/event.ex +++ b/lib/graphql/resolvers/event.ex @@ -224,9 +224,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do # See https://github.com/absinthe-graphql/absinthe/issues/490 with args <- Map.put(args, :options, args[:options] || %{}), {:ok, %Event{} = event} <- Events.get_event_with_preload(event_id), - organizer_actor_id <- args |> Map.get(:organizer_actor_id, event.organizer_actor_id), - {:is_owned, %Actor{} = organizer_actor} <- - User.owns_actor(user, organizer_actor_id), + {:old_actor, {:is_owned, %Actor{}}} <- + {:old_actor, User.owns_actor(user, event.organizer_actor_id)}, + new_organizer_actor_id <- args |> Map.get(:organizer_actor_id, event.organizer_actor_id), + {:new_actor, {:is_owned, %Actor{} = organizer_actor}} <- + {:new_actor, User.owns_actor(user, new_organizer_actor_id)}, args <- Map.put(args, :organizer_actor, organizer_actor), {:ok, %Activity{data: %{"object" => %{"type" => "Event"}}}, %Event{} = event} <- API.Events.update_event(args, event) do @@ -235,8 +237,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do {:error, :event_not_found} -> {:error, dgettext("errors", "Event not found")} - {:is_owned, nil} -> - {:error, dgettext("errors", "User doesn't own profile")} + {:old_actor, _} -> + {:error, dgettext("errors", "You can't edit this event.")} + + {:new_actor, _} -> + {:error, dgettext("errors", "You can't attribute this new event to this profile.")} {:error, _, %Ecto.Changeset{} = error, _} -> {:error, error} diff --git a/test/graphql/resolvers/event_test.exs b/test/graphql/resolvers/event_test.exs index 6f75e70d5..3cd7bf25b 100644 --- a/test/graphql/resolvers/event_test.exs +++ b/test/graphql/resolvers/event_test.exs @@ -666,6 +666,40 @@ defmodule Mobilizon.Web.Resolvers.EventTest do assert hd(json_response(res, 200)["errors"])["message"] == "Event not found" end + test "update_event/3 should check the user can change the organizer", %{ + conn: conn, + actor: actor, + user: user + } do + event = insert(:event, organizer_actor: actor) + actor2 = insert(:actor) + + mutation = """ + mutation { + updateEvent( + title: "my event updated", + event_id: #{event.id} + organizer_actor_id: #{actor2.id} + ) { + title, + uuid, + tags { + title, + slug + } + } + } + """ + + res = + conn + |> auth_conn(user) + |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + + assert hd(json_response(res, 200)["errors"])["message"] == + "You can't attribute this new event to this profile." + end + test "update_event/3 should check the user is the organizer", %{ conn: conn, actor: _actor, @@ -694,7 +728,39 @@ defmodule Mobilizon.Web.Resolvers.EventTest do |> auth_conn(user) |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) - assert hd(json_response(res, 200)["errors"])["message"] == "User doesn't own profile" + assert hd(json_response(res, 200)["errors"])["message"] == "You can't edit this event." + end + + test "update_event/3 should check the user is the organizer also when it's changed", %{ + conn: conn, + actor: actor, + user: user + } do + event = insert(:event) + + mutation = """ + mutation { + updateEvent( + title: "my event updated", + event_id: #{event.id}, + organizer_actor_id: #{actor.id} + ) { + title, + uuid, + tags { + title, + slug + } + } + } + """ + + res = + conn + |> auth_conn(user) + |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + + assert hd(json_response(res, 200)["errors"])["message"] == "You can't edit this event." end test "update_event/3 should check end time is after the beginning time", %{