From ab56d3e607029c9bc96ad03fc6850d02c59fb7a0 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 14 Jan 2019 15:56:07 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8D=20Implement=20basic=20event=20visi?= =?UTF-8?q?bility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://framagit.org/framasoft/mobilizon/wikis/spec/Event#visibility Also brings support for event status (tentative/confirmed/cancelled) Closes #56 Signed-off-by: Thomas Citharel --- lib/mobilizon/events/comment.ex | 14 +++- lib/mobilizon/events/event.ex | 29 ++++++--- lib/mobilizon/events/events.ex | 64 +++++++++++-------- .../20180702150922_add_address_type.exs | 4 +- .../20190103150805_fix_event_visibility.exs | 27 ++++++++ .../resolvers/event_resolver_test.exs | 58 +++++++++++++++++ test/support/factory.ex | 2 +- 7 files changed, 157 insertions(+), 41 deletions(-) create mode 100644 priv/repo/migrations/20190103150805_fix_event_visibility.exs diff --git a/lib/mobilizon/events/comment.ex b/lib/mobilizon/events/comment.ex index d49e459ed..33127ccdd 100644 --- a/lib/mobilizon/events/comment.ex +++ b/lib/mobilizon/events/comment.ex @@ -1,3 +1,14 @@ +import EctoEnum + +defenum(Mobilizon.Events.CommentVisibilityEnum, :comment_visibility_type, [ + :public, + :local, + :group, + :unlisted, + :moderated, + :private +]) + defmodule Mobilizon.Events.Comment do @moduledoc """ An actor comment (for instance on an event or on a group) @@ -14,6 +25,7 @@ defmodule Mobilizon.Events.Comment do field(:text, :string) field(:url, :string) field(:local, :boolean, default: true) + field(:visibility, :integer) field(:uuid, Ecto.UUID) belongs_to(:actor, Actor, foreign_key: :actor_id) belongs_to(:attributed_to, Actor, foreign_key: :attributed_to_id) @@ -38,7 +50,7 @@ defmodule Mobilizon.Events.Comment do else: "#{MobilizonWeb.Endpoint.url()}/comments/#{uuid}" comment - |> cast(attrs, [ + |> Ecto.Changeset.cast(attrs, [ :url, :text, :actor_id, diff --git a/lib/mobilizon/events/event.ex b/lib/mobilizon/events/event.ex index 5f76d6e50..c93616e07 100644 --- a/lib/mobilizon/events/event.ex +++ b/lib/mobilizon/events/event.ex @@ -1,5 +1,19 @@ import EctoEnum -defenum(AddressTypeEnum, :address_type, [:physical, :url, :phone, :other]) +defenum(Mobilizon.Events.AddressTypeEnum, :address_type, [:physical, :url, :phone, :other]) + +defenum(Mobilizon.Events.EventVisibilityEnum, :event_visibility_type, [ + :public, + :unlisted, + :private, + :moderated, + :invite +]) + +defenum(Mobilizon.Events.EventStatusEnum, :event_status_type, [ + :tentative, + :confirmed, + :cancelled +]) defmodule Mobilizon.Events.Event do @moduledoc """ @@ -18,17 +32,13 @@ defmodule Mobilizon.Events.Event do field(:description, :string) field(:ends_on, Timex.Ecto.DateTimeWithTimezone) field(:title, :string) - # ??? - field(:state, :integer, default: 0) - # Event status: TENTATIVE 1, CONFIRMED 2, CANCELLED 3 - field(:status, :integer, default: 0) - # If the event is public or private - field(:public, :boolean, default: true) + field(:status, Mobilizon.Events.EventStatusEnum, default: :confirmed) + field(:visibility, Mobilizon.Events.EventVisibilityEnum, default: :public) field(:thumbnail, :string) field(:large_image, :string) field(:publish_at, Timex.Ecto.DateTimeWithTimezone) field(:uuid, Ecto.UUID, default: Ecto.UUID.generate()) - field(:address_type, AddressTypeEnum, default: :physical) + field(:address_type, Mobilizon.Events.AddressTypeEnum, default: :physical) field(:online_address, :string) field(:phone, :string) belongs_to(:organizer_actor, Actor, foreign_key: :organizer_actor_id) @@ -54,9 +64,8 @@ defmodule Mobilizon.Events.Event do :ends_on, :organizer_actor_id, :category_id, - :state, :status, - :public, + :visibility, :thumbnail, :large_image, :publish_at, diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index 35e24d04e..14c79da9b 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -50,7 +50,7 @@ defmodule Mobilizon.Events do from( e in Event, select: count(e.id), - where: e.local == ^true + where: e.local == ^true and e.visibility == ^:public ) ) end @@ -80,7 +80,7 @@ defmodule Mobilizon.Events do e in Event, join: a in Address, on: a.id == e.physical_address_id, - where: st_dwithin_in_meters(^point, a.geom, ^radius), + where: e.visibility == ^:public and st_dwithin_in_meters(^point, a.geom, ^radius), preload: :organizer_actor ) ) @@ -130,17 +130,20 @@ defmodule Mobilizon.Events do """ @spec get_event_full_by_uuid(String.t()) :: Event.t() def get_event_full_by_uuid(uuid) do - event = Repo.get_by(Event, uuid: uuid) - - Repo.preload(event, [ - :organizer_actor, - :category, - :sessions, - :tracks, - :tags, - :participants, - :physical_address - ]) + from( + e in Event, + where: e.uuid == ^uuid and e.visibility in [^:public, ^:unlisted], + preload: [ + :organizer_actor, + :category, + :sessions, + :tracks, + :tags, + :participants, + :physical_address + ] + ) + |> Repo.one() end @doc """ @@ -166,7 +169,7 @@ defmodule Mobilizon.Events do def get_event_full_by_url(url) do case Repo.one( from(e in Event, - where: e.url == ^url, + where: e.url == ^url and e.visibility in [^:public, ^:unlisted], preload: [ :organizer_actor, :category, @@ -187,17 +190,20 @@ defmodule Mobilizon.Events do Gets an event by it's URL """ def get_event_full_by_url!(url) do - event = Repo.get_by!(Event, url: url) - - Repo.preload(event, [ - :organizer_actor, - :category, - :sessions, - :tracks, - :tags, - :participants, - :physical_address - ]) + Repo.one( + from(e in Event, + where: e.url == ^url and e.visibility in [^:public, ^:unlisted], + preload: [ + :organizer_actor, + :category, + :sessions, + :tracks, + :tags, + :participants, + :physical_address + ] + ) + ) end @doc """ @@ -211,7 +217,11 @@ defmodule Mobilizon.Events do """ def list_events(page \\ nil, limit \\ nil) do query = - from(e in Event, preload: [:organizer_actor]) + from( + e in Event, + where: e.visibility == ^:public, + preload: [:organizer_actor] + ) |> paginate(page, limit) Repo.all(query) @@ -228,7 +238,7 @@ defmodule Mobilizon.Events do query = from(e in Event, - where: ilike(e.title, ^like_sanitize(name)), + where: e.visibility == ^:public and ilike(e.title, ^like_sanitize(name)), preload: [:organizer_actor] ) |> paginate(page, limit) diff --git a/priv/repo/migrations/20180702150922_add_address_type.exs b/priv/repo/migrations/20180702150922_add_address_type.exs index d89f75097..d9085e08d 100644 --- a/priv/repo/migrations/20180702150922_add_address_type.exs +++ b/priv/repo/migrations/20180702150922_add_address_type.exs @@ -2,7 +2,7 @@ defmodule Mobilizon.Repo.Migrations.AddAddressType do use Ecto.Migration def up do - AddressTypeEnum.create_type + Mobilizon.Events.AddressTypeEnum.create_type alter table(:events) do add :address_type, :address_type add :online_address, :string @@ -21,7 +21,7 @@ defmodule Mobilizon.Repo.Migrations.AddAddressType do remove :online_address remove :phone end - AddressTypeEnum.drop_type + Mobilizon.Events.AddressTypeEnum.drop_type drop constraint(:events, "events_physical_address_id_fkey") rename table(:events), :physical_address_id, to: :address_id alter table(:events) do diff --git a/priv/repo/migrations/20190103150805_fix_event_visibility.exs b/priv/repo/migrations/20190103150805_fix_event_visibility.exs new file mode 100644 index 000000000..9a7b6ba6b --- /dev/null +++ b/priv/repo/migrations/20190103150805_fix_event_visibility.exs @@ -0,0 +1,27 @@ +defmodule Mobilizon.Repo.Migrations.FixEventVisibility do + use Ecto.Migration + + def up do + Mobilizon.Events.EventVisibilityEnum.create_type + Mobilizon.Events.EventStatusEnum.create_type + alter table(:events) do + remove(:public) + remove(:status) + remove(:state) + add(:visibility, Mobilizon.Events.EventVisibilityEnum.type()) + add(:status, Mobilizon.Events.EventStatusEnum.type()) + end + end + + def down do + alter table(:events) do + remove(:visibility) + remove(:status) + add(:state, :integer, null: false, default: 0) + add(:public, :boolean, null: false, default: false) + add(:status, :integer, null: false, default: 0) + end + Mobilizon.Events.EventVisibilityEnum.drop_type + Mobilizon.Events.EventStatusEnum.drop_type + end +end diff --git a/test/mobilizon_web/resolvers/event_resolver_test.exs b/test/mobilizon_web/resolvers/event_resolver_test.exs index 5af5f12e9..e82a1e10b 100644 --- a/test/mobilizon_web/resolvers/event_resolver_test.exs +++ b/test/mobilizon_web/resolvers/event_resolver_test.exs @@ -241,5 +241,63 @@ defmodule MobilizonWeb.Resolvers.EventResolverTest do assert json_response(res, 200)["data"]["events"] |> length == 0 end + + test "list_events/3 doesn't list private events", context do + insert(:event, visibility: :private) + insert(:event, visibility: :unlisted) + insert(:event, visibility: :moderated) + insert(:event, visibility: :invite) + + query = """ + { + events { + uuid, + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + + assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == [] + end + + test "find_event/3 returns an unlisted event", context do + event = insert(:event, visibility: :unlisted) + + query = """ + { + event(uuid: "#{event.uuid}") { + uuid, + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + + assert json_response(res, 200)["data"]["event"]["uuid"] == to_string(event.uuid) + end + + test "find_event/3 doesn't return a private event", context do + event = insert(:event, visibility: :private) + + query = """ + { + event(uuid: "#{event.uuid}") { + uuid, + } + } + """ + + res = + context.conn + |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + + assert json_response(res, 200)["errors"] |> hd |> Map.get("message") == + "Event with UUID #{event.uuid} not found" + end end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 321b43d7f..3e52914ac 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -105,7 +105,7 @@ defmodule Mobilizon.Factory do organizer_actor: actor, category: build(:category), physical_address: build(:address), - public: true, + visibility: :public, url: "@#{actor.url}/#{Ecto.UUID.generate()}" } end