Refactor Core things, including Ecto handling, ActivityPub & Transmogrifier modules

* Data doesn't need anymore to be converted to ActivityStream format to
be saved (this was taken from Pleroma and not at all a good idea here)
* Everything saved when creating an event is inserted into PostgreSQL in
a single transaction
This commit is contained in:
Thomas Citharel
2019-10-25 17:43:37 +02:00
parent 814cfbc8eb
commit cc820d6b63
69 changed files with 1881 additions and 1424 deletions

View File

@@ -6,22 +6,31 @@ defmodule Mobilizon.Events.Event do
use Ecto.Schema
import Ecto.Changeset
alias Ecto.Changeset
alias Mobilizon.Actors.Actor
alias Mobilizon.Addresses.Address
alias Mobilizon.Addresses
alias Mobilizon.Events.{
EventOptions,
EventStatus,
EventVisibility,
JoinOptions,
EventParticipantStats,
Participant,
Session,
Tag,
Track
}
alias Mobilizon.Media
alias Mobilizon.Media.Picture
alias Mobilizon.Mention
alias MobilizonWeb.Endpoint
alias MobilizonWeb.Router.Helpers, as: Routes
@type t :: %__MODULE__{
url: String.t(),
@@ -47,30 +56,15 @@ defmodule Mobilizon.Events.Event do
picture: Picture.t(),
tracks: [Track.t()],
sessions: [Session.t()],
mentions: [Mention.t()],
tags: [Tag.t()],
participants: [Actor.t()]
}
@required_attrs [:title, :begins_on, :organizer_actor_id, :url, :uuid]
@update_required_attrs [:title, :begins_on, :organizer_actor_id]
@required_attrs @update_required_attrs ++ [:url, :uuid]
@optional_attrs [
:slug,
:description,
:ends_on,
:category,
:status,
:draft,
:visibility,
:publish_at,
:online_address,
:phone_address,
:picture_id,
:physical_address_id
]
@attrs @required_attrs ++ @optional_attrs
@update_required_attrs @required_attrs
@update_optional_attrs [
:slug,
:description,
:ends_on,
@@ -85,7 +79,9 @@ defmodule Mobilizon.Events.Event do
:picture_id,
:physical_address_id
]
@update_attrs @update_required_attrs ++ @update_optional_attrs
@attrs @required_attrs ++ @optional_attrs
@update_attrs @update_required_attrs ++ @optional_attrs
schema "events" do
field(:url, :string)
@@ -105,13 +101,15 @@ defmodule Mobilizon.Events.Event do
field(:phone_address, :string)
field(:category, :string)
embeds_one(:options, EventOptions, on_replace: :update)
embeds_one(:options, EventOptions, on_replace: :delete)
embeds_one(:participant_stats, EventParticipantStats, on_replace: :update)
belongs_to(:organizer_actor, Actor, foreign_key: :organizer_actor_id)
belongs_to(:attributed_to, Actor, foreign_key: :attributed_to_id)
belongs_to(:physical_address, Address)
belongs_to(:picture, Picture)
belongs_to(:physical_address, Address, on_replace: :update)
belongs_to(:picture, Picture, on_replace: :update)
has_many(:tracks, Track)
has_many(:sessions, Session)
has_many(:mentions, Mention)
many_to_many(:tags, Tag, join_through: "events_tags", on_replace: :delete)
many_to_many(:participants, Actor, join_through: Participant)
@@ -119,28 +117,41 @@ defmodule Mobilizon.Events.Event do
end
@doc false
@spec changeset(t, map) :: Ecto.Changeset.t()
@spec changeset(t, map) :: Changeset.t()
def changeset(%__MODULE__{} = event, attrs) do
attrs = Map.update(attrs, :uuid, Ecto.UUID.generate(), & &1)
attrs = Map.update(attrs, :url, Routes.page_url(Endpoint, :event, attrs.uuid), & &1)
event
|> cast(attrs, @attrs)
|> cast_embed(:options)
|> common_changeset(attrs)
|> put_creator_if_published(:create)
|> validate_required(@required_attrs)
|> validate_lengths()
end
@doc false
@spec update_changeset(t, map) :: Ecto.Changeset.t()
@spec update_changeset(t, map) :: Changeset.t()
def update_changeset(%__MODULE__{} = event, attrs) do
event
|> Ecto.Changeset.cast(attrs, @update_attrs)
|> cast_embed(:options)
|> put_tags(attrs)
|> cast(attrs, @update_attrs)
|> common_changeset(attrs)
|> put_creator_if_published(:update)
|> validate_required(@update_required_attrs)
|> validate_lengths()
end
@spec validate_lengths(Ecto.Changeset.t()) :: Ecto.Changeset.t()
defp validate_lengths(%Ecto.Changeset{} = changeset) do
@spec common_changeset(Changeset.t(), map) :: Changeset.t()
defp common_changeset(%Changeset{} = changeset, attrs) do
changeset
|> cast_embed(:options)
|> put_tags(attrs)
|> put_address(attrs)
|> put_picture(attrs)
end
@spec validate_lengths(Changeset.t()) :: Changeset.t()
defp validate_lengths(%Changeset{} = changeset) do
changeset
|> validate_length(:title, min: 3, max: 200)
|> validate_length(:online_address, min: 3, max: 2000)
@@ -161,7 +172,80 @@ defmodule Mobilizon.Events.Event do
def can_be_managed_by(_event, _actor), do: {:event_can_be_managed, false}
@spec put_tags(Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
defp put_tags(changeset, %{"tags" => tags}), do: put_assoc(changeset, :tags, tags)
defp put_tags(changeset, _), do: changeset
@spec put_tags(Changeset.t(), map) :: Changeset.t()
defp put_tags(%Changeset{} = changeset, %{tags: tags}),
do: put_assoc(changeset, :tags, Enum.map(tags, &process_tag/1))
defp put_tags(%Changeset{} = changeset, _), do: changeset
# We need a changeset instead of a raw struct because of slug which is generated in changeset
defp process_tag(tag) do
Tag.changeset(%Tag{}, tag)
end
# In case the provided addresses is an existing one
@spec put_address(Changeset.t(), map) :: Changeset.t()
defp put_address(%Changeset{} = changeset, %{physical_address: %{id: id} = _physical_address}) do
case Addresses.get_address!(id) do
%Address{} = address ->
put_assoc(changeset, :physical_address, address)
_ ->
changeset
end
end
# In case it's a new address
defp put_address(%Changeset{} = changeset, _attrs) do
cast_assoc(changeset, :physical_address)
end
# In case the provided picture is an existing one
@spec put_picture(Changeset.t(), map) :: Changeset.t()
defp put_picture(%Changeset{} = changeset, %{picture: %{picture_id: id} = _picture}) do
case Media.get_picture!(id) do
%Picture{} = picture ->
put_assoc(changeset, :picture, picture)
_ ->
changeset
end
end
# In case it's a new picture
defp put_picture(%Changeset{} = changeset, _attrs) do
cast_assoc(changeset, :picture)
end
# Created or updated with draft parameter: don't publish
defp put_creator_if_published(
%Changeset{changes: %{draft: true}} = changeset,
_action
) do
cast_embed(changeset, :participant_stats)
end
# Created with any other value: publish
defp put_creator_if_published(
%Changeset{} = changeset,
:create
) do
changeset
|> put_embed(:participant_stats, %{creator: 1})
end
# Updated from draft false to true: publish
defp put_creator_if_published(
%Changeset{
data: %{draft: false},
changes: %{draft: true}
} = changeset,
:update
) do
changeset
|> put_embed(:participant_stats, %{creator: 1})
end
defp put_creator_if_published(%Changeset{} = changeset, _),
do: cast_embed(changeset, :participant_stats)
end