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:
@@ -13,6 +13,7 @@ defmodule Mobilizon.Actors.Actor do
|
||||
alias Mobilizon.Media.File
|
||||
alias Mobilizon.Reports.{Note, Report}
|
||||
alias Mobilizon.Users.User
|
||||
alias Mobilizon.Mention
|
||||
|
||||
alias MobilizonWeb.Endpoint
|
||||
alias MobilizonWeb.Router.Helpers, as: Routes
|
||||
@@ -46,6 +47,7 @@ defmodule Mobilizon.Actors.Actor do
|
||||
created_reports: [Report.t()],
|
||||
subject_reports: [Report.t()],
|
||||
report_notes: [Note.t()],
|
||||
mentions: [Mention.t()],
|
||||
memberships: [t]
|
||||
}
|
||||
|
||||
@@ -139,6 +141,7 @@ defmodule Mobilizon.Actors.Actor do
|
||||
has_many(:created_reports, Report, foreign_key: :reporter_id)
|
||||
has_many(:subject_reports, Report, foreign_key: :reported_id)
|
||||
has_many(:report_notes, Note, foreign_key: :moderator_id)
|
||||
has_many(:mentions, Mention)
|
||||
many_to_many(:memberships, __MODULE__, join_through: Member)
|
||||
|
||||
timestamps()
|
||||
|
||||
@@ -88,7 +88,10 @@ defmodule Mobilizon.Actors do
|
||||
"""
|
||||
@spec get_actor_by_url(String.t(), boolean) ::
|
||||
{:ok, Actor.t()} | {:error, :actor_not_found}
|
||||
def get_actor_by_url(url, preload \\ false) do
|
||||
def get_actor_by_url(url, preload \\ false)
|
||||
def get_actor_by_url(nil, _preload), do: {:error, :actor_not_found}
|
||||
|
||||
def get_actor_by_url(url, preload) do
|
||||
case Repo.get_by(Actor, url: url) do
|
||||
nil ->
|
||||
{:error, :actor_not_found}
|
||||
|
||||
@@ -65,8 +65,7 @@ defmodule Mobilizon.Addresses.Address do
|
||||
|
||||
@spec set_url(Ecto.Changeset.t()) :: Ecto.Changeset.t()
|
||||
defp set_url(%Ecto.Changeset{changes: changes} = changeset) do
|
||||
uuid = Ecto.UUID.generate()
|
||||
url = Map.get(changes, :url, "#{MobilizonWeb.Endpoint.url()}/address/#{uuid}")
|
||||
url = Map.get(changes, :url, "#{MobilizonWeb.Endpoint.url()}/address/#{Ecto.UUID.generate()}")
|
||||
|
||||
put_change(changeset, :url, url)
|
||||
end
|
||||
|
||||
@@ -8,7 +8,8 @@ defmodule Mobilizon.Events.Comment do
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events.{Comment, CommentVisibility, Event}
|
||||
alias Mobilizon.Events.{Comment, CommentVisibility, Event, Tag}
|
||||
alias Mobilizon.Mention
|
||||
|
||||
alias MobilizonWeb.Endpoint
|
||||
alias MobilizonWeb.Router.Helpers, as: Routes
|
||||
@@ -22,6 +23,8 @@ defmodule Mobilizon.Events.Comment do
|
||||
actor: Actor.t(),
|
||||
attributed_to: Actor.t(),
|
||||
event: Event.t(),
|
||||
tags: [Tag.t()],
|
||||
mentions: [Mention.t()],
|
||||
in_reply_to_comment: t,
|
||||
origin_comment: t
|
||||
}
|
||||
@@ -42,6 +45,8 @@ defmodule Mobilizon.Events.Comment do
|
||||
belongs_to(:event, Event, foreign_key: :event_id)
|
||||
belongs_to(:in_reply_to_comment, Comment, foreign_key: :in_reply_to_comment_id)
|
||||
belongs_to(:origin_comment, Comment, foreign_key: :origin_comment_id)
|
||||
many_to_many(:tags, Tag, join_through: "comments_tags", on_replace: :delete)
|
||||
has_many(:mentions, Mention)
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
@@ -57,16 +62,45 @@ defmodule Mobilizon.Events.Comment do
|
||||
@doc false
|
||||
@spec changeset(t, map) :: Ecto.Changeset.t()
|
||||
def changeset(%__MODULE__{} = comment, attrs) do
|
||||
uuid = attrs["uuid"] || Ecto.UUID.generate()
|
||||
url = attrs["url"] || generate_url(uuid)
|
||||
uuid = Map.get(attrs, :uuid) || Ecto.UUID.generate()
|
||||
url = Map.get(attrs, :url) || generate_url(uuid)
|
||||
|
||||
comment
|
||||
|> cast(attrs, @attrs)
|
||||
|> put_change(:uuid, uuid)
|
||||
|> put_change(:url, url)
|
||||
|> put_tags(attrs)
|
||||
|> put_mentions(attrs)
|
||||
|> validate_required(@required_attrs)
|
||||
end
|
||||
|
||||
@spec generate_url(String.t()) :: String.t()
|
||||
defp generate_url(uuid), do: Routes.page_url(Endpoint, :comment, uuid)
|
||||
|
||||
@spec put_tags(Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
|
||||
defp put_tags(changeset, %{"tags" => tags}),
|
||||
do: put_assoc(changeset, :tags, Enum.map(tags, &process_tag/1))
|
||||
|
||||
defp put_tags(changeset, %{tags: tags}),
|
||||
do: put_assoc(changeset, :tags, Enum.map(tags, &process_tag/1))
|
||||
|
||||
defp put_tags(changeset, _), do: changeset
|
||||
|
||||
@spec put_mentions(Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
|
||||
defp put_mentions(changeset, %{"mentions" => mentions}),
|
||||
do: put_assoc(changeset, :mentions, Enum.map(mentions, &process_mention/1))
|
||||
|
||||
defp put_mentions(changeset, %{mentions: mentions}),
|
||||
do: put_assoc(changeset, :mentions, Enum.map(mentions, &process_mention/1))
|
||||
|
||||
defp put_mentions(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
|
||||
|
||||
defp process_mention(tag) do
|
||||
Mention.changeset(%Mention{}, tag)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
44
lib/mobilizon/events/event_participant_stats.ex
Normal file
44
lib/mobilizon/events/event_participant_stats.ex
Normal file
@@ -0,0 +1,44 @@
|
||||
defmodule Mobilizon.Events.EventParticipantStats do
|
||||
@moduledoc """
|
||||
Participation stats on event
|
||||
"""
|
||||
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
not_approved: integer(),
|
||||
rejected: integer(),
|
||||
participant: integer(),
|
||||
moderator: integer(),
|
||||
administrator: integer(),
|
||||
creator: integer()
|
||||
}
|
||||
|
||||
@attrs [
|
||||
:not_approved,
|
||||
:rejected,
|
||||
:participant,
|
||||
:moderator,
|
||||
:administrator,
|
||||
:moderator,
|
||||
:creator
|
||||
]
|
||||
|
||||
@primary_key false
|
||||
@derive Jason.Encoder
|
||||
embedded_schema do
|
||||
field(:not_approved, :integer, default: 0)
|
||||
field(:rejected, :integer, default: 0)
|
||||
field(:participant, :integer, default: 0)
|
||||
field(:moderator, :integer, default: 0)
|
||||
field(:administrator, :integer, default: 0)
|
||||
field(:creator, :integer, default: 0)
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec changeset(t, map) :: Ecto.Changeset.t()
|
||||
def changeset(%__MODULE__{} = event_options, attrs) do
|
||||
cast(event_options, attrs, @attrs)
|
||||
end
|
||||
end
|
||||
@@ -7,6 +7,7 @@ defmodule Mobilizon.Events do
|
||||
|
||||
import Ecto.Query
|
||||
import EctoEnum
|
||||
alias Ecto.{Multi, Changeset}
|
||||
|
||||
import Mobilizon.Storage.Ecto
|
||||
|
||||
@@ -17,6 +18,7 @@ defmodule Mobilizon.Events do
|
||||
alias Mobilizon.Events.{
|
||||
Comment,
|
||||
Event,
|
||||
EventParticipantStats,
|
||||
FeedToken,
|
||||
Participant,
|
||||
Session,
|
||||
@@ -82,6 +84,8 @@ defmodule Mobilizon.Events do
|
||||
|
||||
@event_preloads [
|
||||
:organizer_actor,
|
||||
:attributed_to,
|
||||
:mentions,
|
||||
:sessions,
|
||||
:tracks,
|
||||
:tags,
|
||||
@@ -90,7 +94,7 @@ defmodule Mobilizon.Events do
|
||||
:picture
|
||||
]
|
||||
|
||||
@comment_preloads [:actor, :attributed_to, :in_reply_to_comment]
|
||||
@comment_preloads [:actor, :attributed_to, :in_reply_to_comment, :tags, :mentions]
|
||||
|
||||
@doc """
|
||||
Gets a single event.
|
||||
@@ -235,81 +239,103 @@ defmodule Mobilizon.Events do
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def get_or_create_event(%{"url" => url} = attrs) do
|
||||
case Repo.get_by(Event, url: url) do
|
||||
%Event{} = event -> {:ok, Repo.preload(event, @event_preloads)}
|
||||
nil -> create_event(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates an event.
|
||||
"""
|
||||
@spec create_event(map) :: {:ok, Event.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_event(map) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
||||
def create_event(attrs \\ %{}) do
|
||||
with {:ok, %Event{draft: false} = event} <- do_create_event(attrs),
|
||||
{:ok, %Participant{} = _participant} <-
|
||||
create_participant(%{
|
||||
actor_id: event.organizer_actor_id,
|
||||
role: :creator,
|
||||
event_id: event.id
|
||||
}) do
|
||||
with {:ok, %{insert: %Event{} = event}} <- do_create_event(attrs),
|
||||
%Event{} = event <- Repo.preload(event, @event_preloads) do
|
||||
Task.start(fn -> Search.insert_search_event(event) end)
|
||||
{:ok, event}
|
||||
else
|
||||
# We don't create a creator participant if the event is a draft
|
||||
{:ok, %Event{draft: true} = event} -> {:ok, event}
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
@spec do_create_event(map) :: {:ok, Event.t()} | {:error, Ecto.Changeset.t()}
|
||||
# We start by inserting the event and then insert a first participant if the event is not a draft
|
||||
@spec do_create_event(map) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
||||
defp do_create_event(attrs) do
|
||||
with {:ok, %Event{} = event} <-
|
||||
%Event{}
|
||||
|> Event.changeset(attrs)
|
||||
|> Ecto.Changeset.put_assoc(:tags, Map.get(attrs, "tags", []))
|
||||
|> Repo.insert(),
|
||||
%Event{} = event <-
|
||||
Repo.preload(event, [:tags, :organizer_actor, :physical_address, :picture]) do
|
||||
{:ok, event}
|
||||
end
|
||||
Multi.new()
|
||||
|> Multi.insert(:insert, Event.changeset(%Event{}, attrs))
|
||||
|> Multi.run(:write, fn _repo, %{insert: %Event{draft: draft} = event} ->
|
||||
with {:is_draft, false} <- {:is_draft, draft},
|
||||
{:ok, %Participant{} = participant} <-
|
||||
create_participant(
|
||||
%{
|
||||
event_id: event.id,
|
||||
role: :creator,
|
||||
actor_id: event.organizer_actor_id
|
||||
},
|
||||
false
|
||||
) do
|
||||
{:ok, participant}
|
||||
else
|
||||
{:is_draft, true} -> {:ok, nil}
|
||||
err -> err
|
||||
end
|
||||
end)
|
||||
|> Repo.transaction()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates an event.
|
||||
|
||||
We start by updating the event and then insert a first participant if the event is not a draft anymore
|
||||
"""
|
||||
@spec update_event(Event.t(), map) :: {:ok, Event.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_event(
|
||||
%Event{draft: old_draft_status, id: event_id, organizer_actor_id: organizer_actor_id} =
|
||||
old_event,
|
||||
attrs
|
||||
) do
|
||||
with %Ecto.Changeset{changes: changes} = changeset <-
|
||||
old_event |> Repo.preload(:tags) |> Event.update_changeset(attrs) do
|
||||
with {:ok, %Event{draft: new_draft_status} = new_event} <- Repo.update(changeset) do
|
||||
# If the event is no longer a draft
|
||||
if old_draft_status == true && new_draft_status == false do
|
||||
{:ok, %Participant{} = _participant} =
|
||||
create_participant(%{
|
||||
event_id: event_id,
|
||||
role: :creator,
|
||||
actor_id: organizer_actor_id
|
||||
})
|
||||
end
|
||||
@spec update_event(Event.t(), map) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
||||
def update_event(%Event{} = old_event, attrs) do
|
||||
with %Changeset{changes: changes} = changeset <-
|
||||
Event.update_changeset(Repo.preload(old_event, :tags), attrs),
|
||||
{:ok, %{update: %Event{} = new_event}} <-
|
||||
Multi.new()
|
||||
|> Multi.update(
|
||||
:update,
|
||||
changeset
|
||||
)
|
||||
|> Multi.run(:write, fn _repo, %{update: %Event{draft: draft} = event} ->
|
||||
with {:is_draft, false} <- {:is_draft, draft},
|
||||
{:ok, %Participant{} = participant} <-
|
||||
create_participant(
|
||||
%{
|
||||
event_id: event.id,
|
||||
role: :creator,
|
||||
actor_id: event.organizer_actor_id
|
||||
},
|
||||
false
|
||||
) do
|
||||
{:ok, participant}
|
||||
else
|
||||
{:is_draft, true} -> {:ok, nil}
|
||||
err -> err
|
||||
end
|
||||
end)
|
||||
|> Repo.transaction() do
|
||||
Cachex.del(:ics, "event_#{new_event.uuid}")
|
||||
|
||||
Cachex.del(:ics, "event_#{new_event.uuid}")
|
||||
Mobilizon.Service.Events.Tool.calculate_event_diff_and_send_notifications(
|
||||
old_event,
|
||||
new_event,
|
||||
changes
|
||||
)
|
||||
|
||||
Mobilizon.Service.Events.Tool.calculate_event_diff_and_send_notifications(
|
||||
old_event,
|
||||
new_event,
|
||||
changes
|
||||
)
|
||||
Task.start(fn -> Search.update_search_event(new_event) end)
|
||||
|
||||
Task.start(fn -> Search.update_search_event(new_event) end)
|
||||
|
||||
{:ok, new_event}
|
||||
end
|
||||
{:ok, Repo.preload(new_event, @event_preloads)}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes an event.
|
||||
"""
|
||||
@spec delete_event(Event.t()) :: {:ok, Event.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_event(Event.t()) :: {:ok, Event.t()} | {:error, Changeset.t()}
|
||||
def delete_event(%Event{} = event), do: Repo.delete(event)
|
||||
|
||||
@doc """
|
||||
@@ -450,7 +476,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Gets an existing tag or creates the new one.
|
||||
"""
|
||||
@spec get_or_create_tag(map) :: {:ok, Tag.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec get_or_create_tag(map) :: {:ok, Tag.t()} | {:error, Changeset.t()}
|
||||
def get_or_create_tag(%{"name" => "#" <> title}) do
|
||||
case Repo.get_by(Tag, title: title) do
|
||||
%Tag{} = tag ->
|
||||
@@ -461,10 +487,24 @@ defmodule Mobilizon.Events do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an existing tag or creates the new one.
|
||||
"""
|
||||
@spec get_or_create_tag(String.t()) :: {:ok, Tag.t()} | {:error, Changeset.t()}
|
||||
def get_or_create_tag(title) do
|
||||
case Repo.get_by(Tag, title: title) do
|
||||
%Tag{} = tag ->
|
||||
{:ok, tag}
|
||||
|
||||
nil ->
|
||||
create_tag(%{"title" => title})
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a tag.
|
||||
"""
|
||||
@spec create_tag(map) :: {:ok, Tag.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_tag(map) :: {:ok, Tag.t()} | {:error, Changeset.t()}
|
||||
def create_tag(attrs \\ %{}) do
|
||||
%Tag{}
|
||||
|> Tag.changeset(attrs)
|
||||
@@ -474,7 +514,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Updates a tag.
|
||||
"""
|
||||
@spec update_tag(Tag.t(), map) :: {:ok, Tag.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec update_tag(Tag.t(), map) :: {:ok, Tag.t()} | {:error, Changeset.t()}
|
||||
def update_tag(%Tag{} = tag, attrs) do
|
||||
tag
|
||||
|> Tag.changeset(attrs)
|
||||
@@ -484,7 +524,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Deletes a tag.
|
||||
"""
|
||||
@spec delete_tag(Tag.t()) :: {:ok, Tag.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_tag(Tag.t()) :: {:ok, Tag.t()} | {:error, Changeset.t()}
|
||||
def delete_tag(%Tag{} = tag), do: Repo.delete(tag)
|
||||
|
||||
@doc """
|
||||
@@ -524,7 +564,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Creates a relation between two tags.
|
||||
"""
|
||||
@spec create_tag_relation(map) :: {:ok, TagRelation.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_tag_relation(map) :: {:ok, TagRelation.t()} | {:error, Changeset.t()}
|
||||
def create_tag_relation(attrs \\ {}) do
|
||||
%TagRelation{}
|
||||
|> TagRelation.changeset(attrs)
|
||||
@@ -538,7 +578,7 @@ defmodule Mobilizon.Events do
|
||||
Removes a tag relation.
|
||||
"""
|
||||
@spec delete_tag_relation(TagRelation.t()) ::
|
||||
{:ok, TagRelation.t()} | {:error, Ecto.Changeset.t()}
|
||||
{:ok, TagRelation.t()} | {:error, Changeset.t()}
|
||||
def delete_tag_relation(%TagRelation{} = tag_relation) do
|
||||
Repo.delete(tag_relation)
|
||||
end
|
||||
@@ -763,7 +803,7 @@ defmodule Mobilizon.Events do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Counts participant participants.
|
||||
Counts participant participants (participants with no extra role)
|
||||
"""
|
||||
@spec count_participant_participants(integer | String.t()) :: integer
|
||||
def count_participant_participants(event_id) do
|
||||
@@ -773,17 +813,6 @@ defmodule Mobilizon.Events do
|
||||
|> Repo.aggregate(:count, :id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Counts unapproved participants.
|
||||
"""
|
||||
@spec count_unapproved_participants(integer | String.t()) :: integer
|
||||
def count_unapproved_participants(event_id) do
|
||||
event_id
|
||||
|> count_participants_query()
|
||||
|> filter_unapproved_role()
|
||||
|> Repo.aggregate(:count, :id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Counts rejected participants.
|
||||
"""
|
||||
@@ -805,12 +834,40 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Creates a participant.
|
||||
"""
|
||||
@spec create_participant(map) :: {:ok, Participant.t()} | {:error, Ecto.Changeset.t()}
|
||||
def create_participant(attrs \\ %{}) do
|
||||
with {:ok, %Participant{} = participant} <-
|
||||
%Participant{}
|
||||
|> Participant.changeset(attrs)
|
||||
|> Repo.insert() do
|
||||
@spec create_participant(map) :: {:ok, Participant.t()} | {:error, Changeset.t()}
|
||||
def create_participant(attrs \\ %{}, update_event_participation_stats \\ true) do
|
||||
with {:ok, %{participant: %Participant{} = participant}} <-
|
||||
Multi.new()
|
||||
|> Multi.insert(:participant, Participant.changeset(%Participant{}, attrs))
|
||||
|> Multi.run(:update_event_participation_stats, fn _repo,
|
||||
%{
|
||||
participant:
|
||||
%Participant{
|
||||
role: role,
|
||||
event_id: event_id
|
||||
} = _participant
|
||||
} ->
|
||||
with {:update_event_participation_stats, true} <-
|
||||
{:update_event_participation_stats, update_event_participation_stats},
|
||||
{:ok, %Event{} = event} <- get_event(event_id),
|
||||
%EventParticipantStats{} = participant_stats <-
|
||||
Map.get(event, :participant_stats),
|
||||
%EventParticipantStats{} = participant_stats <-
|
||||
Map.update(participant_stats, role, 0, &(&1 + 1)),
|
||||
{:ok, %Event{} = event} <-
|
||||
event
|
||||
|> Event.update_changeset(%{
|
||||
participant_stats: Map.from_struct(participant_stats)
|
||||
})
|
||||
|> Repo.update() do
|
||||
{:ok, event}
|
||||
else
|
||||
{:update_event_participation_stats, false} -> {:ok, nil}
|
||||
{:error, :event_not_found} -> {:error, :event_not_found}
|
||||
err -> {:error, err}
|
||||
end
|
||||
end)
|
||||
|> Repo.transaction() do
|
||||
{:ok, Repo.preload(participant, [:event, :actor])}
|
||||
end
|
||||
end
|
||||
@@ -819,7 +876,7 @@ defmodule Mobilizon.Events do
|
||||
Updates a participant.
|
||||
"""
|
||||
@spec update_participant(Participant.t(), map) ::
|
||||
{:ok, Participant.t()} | {:error, Ecto.Changeset.t()}
|
||||
{:ok, Participant.t()} | {:error, Changeset.t()}
|
||||
def update_participant(%Participant{} = participant, attrs) do
|
||||
participant
|
||||
|> Participant.changeset(attrs)
|
||||
@@ -830,7 +887,7 @@ defmodule Mobilizon.Events do
|
||||
Deletes a participant.
|
||||
"""
|
||||
@spec delete_participant(Participant.t()) ::
|
||||
{:ok, Participant.t()} | {:error, Ecto.Changeset.t()}
|
||||
{:ok, Participant.t()} | {:error, Changeset.t()}
|
||||
def delete_participant(%Participant{} = participant), do: Repo.delete(participant)
|
||||
|
||||
@doc """
|
||||
@@ -843,7 +900,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Creates a session.
|
||||
"""
|
||||
@spec create_session(map) :: {:ok, Session.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_session(map) :: {:ok, Session.t()} | {:error, Changeset.t()}
|
||||
def create_session(attrs \\ %{}) do
|
||||
%Session{}
|
||||
|> Session.changeset(attrs)
|
||||
@@ -853,7 +910,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Updates a session.
|
||||
"""
|
||||
@spec update_session(Session.t(), map) :: {:ok, Session.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec update_session(Session.t(), map) :: {:ok, Session.t()} | {:error, Changeset.t()}
|
||||
def update_session(%Session{} = session, attrs) do
|
||||
session
|
||||
|> Session.changeset(attrs)
|
||||
@@ -863,7 +920,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Deletes a session.
|
||||
"""
|
||||
@spec delete_session(Session.t()) :: {:ok, Session.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_session(Session.t()) :: {:ok, Session.t()} | {:error, Changeset.t()}
|
||||
def delete_session(%Session{} = session), do: Repo.delete(session)
|
||||
|
||||
@doc """
|
||||
@@ -892,7 +949,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Creates a track.
|
||||
"""
|
||||
@spec create_track(map) :: {:ok, Track.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_track(map) :: {:ok, Track.t()} | {:error, Changeset.t()}
|
||||
def create_track(attrs \\ %{}) do
|
||||
%Track{}
|
||||
|> Track.changeset(attrs)
|
||||
@@ -902,7 +959,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Updates a track.
|
||||
"""
|
||||
@spec update_track(Track.t(), map) :: {:ok, Track.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec update_track(Track.t(), map) :: {:ok, Track.t()} | {:error, Changeset.t()}
|
||||
def update_track(%Track{} = track, attrs) do
|
||||
track
|
||||
|> Track.changeset(attrs)
|
||||
@@ -912,7 +969,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Deletes a track.
|
||||
"""
|
||||
@spec delete_track(Track.t()) :: {:ok, Track.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_track(Track.t()) :: {:ok, Track.t()} | {:error, Changeset.t()}
|
||||
def delete_track(%Track{} = track), do: Repo.delete(track)
|
||||
|
||||
@doc """
|
||||
@@ -931,6 +988,13 @@ defmodule Mobilizon.Events do
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single comment.
|
||||
"""
|
||||
@spec get_comment(integer | String.t()) :: Comment.t()
|
||||
def get_comment(nil), do: nil
|
||||
def get_comment(id), do: Repo.get(Comment, id)
|
||||
|
||||
@doc """
|
||||
Gets a single comment.
|
||||
Raises `Ecto.NoResultsError` if the comment does not exist.
|
||||
@@ -994,10 +1058,17 @@ defmodule Mobilizon.Events do
|
||||
|> Repo.preload(@comment_preloads)
|
||||
end
|
||||
|
||||
def get_or_create_comment(%{"url" => url} = attrs) do
|
||||
case Repo.get_by(Comment, url: url) do
|
||||
%Comment{} = comment -> {:ok, Repo.preload(comment, @comment_preloads)}
|
||||
nil -> create_comment(attrs)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a comment.
|
||||
"""
|
||||
@spec create_comment(map) :: {:ok, Comment.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_comment(map) :: {:ok, Comment.t()} | {:error, Changeset.t()}
|
||||
def create_comment(attrs \\ %{}) do
|
||||
with {:ok, %Comment{} = comment} <-
|
||||
%Comment{}
|
||||
@@ -1011,7 +1082,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Updates a comment.
|
||||
"""
|
||||
@spec update_comment(Comment.t(), map) :: {:ok, Comment.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec update_comment(Comment.t(), map) :: {:ok, Comment.t()} | {:error, Changeset.t()}
|
||||
def update_comment(%Comment{} = comment, attrs) do
|
||||
comment
|
||||
|> Comment.changeset(attrs)
|
||||
@@ -1021,7 +1092,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Deletes a comment.
|
||||
"""
|
||||
@spec delete_comment(Comment.t()) :: {:ok, Comment.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_comment(Comment.t()) :: {:ok, Comment.t()} | {:error, Changeset.t()}
|
||||
def delete_comment(%Comment{} = comment), do: Repo.delete(comment)
|
||||
|
||||
@doc """
|
||||
@@ -1097,7 +1168,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Creates a feed token.
|
||||
"""
|
||||
@spec create_feed_token(map) :: {:ok, FeedToken.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec create_feed_token(map) :: {:ok, FeedToken.t()} | {:error, Changeset.t()}
|
||||
def create_feed_token(attrs \\ %{}) do
|
||||
attrs = Map.put(attrs, "token", Ecto.UUID.generate())
|
||||
|
||||
@@ -1110,7 +1181,7 @@ defmodule Mobilizon.Events do
|
||||
Updates a feed token.
|
||||
"""
|
||||
@spec update_feed_token(FeedToken.t(), map) ::
|
||||
{:ok, FeedToken.t()} | {:error, Ecto.Changeset.t()}
|
||||
{:ok, FeedToken.t()} | {:error, Changeset.t()}
|
||||
def update_feed_token(%FeedToken{} = feed_token, attrs) do
|
||||
feed_token
|
||||
|> FeedToken.changeset(attrs)
|
||||
@@ -1120,7 +1191,7 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Deletes a feed token.
|
||||
"""
|
||||
@spec delete_feed_token(FeedToken.t()) :: {:ok, FeedToken.t()} | {:error, Ecto.Changeset.t()}
|
||||
@spec delete_feed_token(FeedToken.t()) :: {:ok, FeedToken.t()} | {:error, Changeset.t()}
|
||||
def delete_feed_token(%FeedToken{} = feed_token), do: Repo.delete(feed_token)
|
||||
|
||||
@doc """
|
||||
@@ -1354,7 +1425,7 @@ defmodule Mobilizon.Events do
|
||||
end
|
||||
|
||||
@spec count_participants_query(integer) :: Ecto.Query.t()
|
||||
defp count_participants_query(event_id) do
|
||||
def count_participants_query(event_id) do
|
||||
from(p in Participant, where: p.event_id == ^event_id)
|
||||
end
|
||||
|
||||
@@ -1385,17 +1456,10 @@ defmodule Mobilizon.Events do
|
||||
end
|
||||
|
||||
defp public_comments_for_actor_query(actor_id) do
|
||||
from(
|
||||
c in Comment,
|
||||
where: c.actor_id == ^actor_id and c.visibility in ^@public_visibility,
|
||||
order_by: [desc: :id],
|
||||
preload: [
|
||||
:actor,
|
||||
:in_reply_to_comment,
|
||||
:origin_comment,
|
||||
:event
|
||||
]
|
||||
)
|
||||
Comment
|
||||
|> where([c], c.actor_id == ^actor_id and c.visibility in ^@public_visibility)
|
||||
|> order_by([c], desc: :id)
|
||||
|> preload_for_comment()
|
||||
end
|
||||
|
||||
@spec list_participants_for_event_query(String.t()) :: Ecto.Query.t()
|
||||
@@ -1498,31 +1562,30 @@ defmodule Mobilizon.Events do
|
||||
|
||||
@spec filter_approved_role(Ecto.Query.t()) :: Ecto.Query.t()
|
||||
defp filter_approved_role(query) do
|
||||
from(p in query, where: p.role not in ^[:not_approved, :rejected])
|
||||
filter_role(query, [:not_approved, :rejected])
|
||||
end
|
||||
|
||||
@spec filter_participant_role(Ecto.Query.t()) :: Ecto.Query.t()
|
||||
defp filter_participant_role(query) do
|
||||
from(p in query, where: p.role == ^:participant)
|
||||
end
|
||||
|
||||
@spec filter_unapproved_role(Ecto.Query.t()) :: Ecto.Query.t()
|
||||
defp filter_unapproved_role(query) do
|
||||
from(p in query, where: p.role == ^:not_approved)
|
||||
filter_role(query, :participant)
|
||||
end
|
||||
|
||||
@spec filter_rejected_role(Ecto.Query.t()) :: Ecto.Query.t()
|
||||
defp filter_rejected_role(query) do
|
||||
from(p in query, where: p.role == ^:rejected)
|
||||
filter_role(query, :rejected)
|
||||
end
|
||||
|
||||
@spec filter_role(Ecto.Query.t(), list(atom())) :: Ecto.Query.t()
|
||||
defp filter_role(query, []), do: query
|
||||
def filter_role(query, []), do: query
|
||||
|
||||
defp filter_role(query, roles) do
|
||||
def filter_role(query, roles) when is_list(roles) do
|
||||
where(query, [p], p.role in ^roles)
|
||||
end
|
||||
|
||||
def filter_role(query, role) when is_atom(role) do
|
||||
from(p in query, where: p.role == ^role)
|
||||
end
|
||||
|
||||
defp participation_filter_begins_on(query, nil, nil),
|
||||
do: participation_order_begins_on_desc(query)
|
||||
|
||||
|
||||
53
lib/mobilizon/mentions/mention.ex
Normal file
53
lib/mobilizon/mentions/mention.ex
Normal file
@@ -0,0 +1,53 @@
|
||||
defmodule Mobilizon.Mention do
|
||||
@moduledoc """
|
||||
The Mentions context.
|
||||
"""
|
||||
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events.Event
|
||||
alias Mobilizon.Events.Comment
|
||||
alias Mobilizon.Storage.Repo
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
silent: boolean,
|
||||
actor: Actor.t(),
|
||||
event: Event.t(),
|
||||
comment: Comment.t()
|
||||
}
|
||||
|
||||
@required_attrs [:actor_id]
|
||||
@optional_attrs [:silent, :event_id, :comment_id]
|
||||
@attrs @required_attrs ++ @optional_attrs
|
||||
|
||||
schema "mentions" do
|
||||
field(:silent, :boolean, default: false)
|
||||
belongs_to(:actor, Actor)
|
||||
belongs_to(:event, Event)
|
||||
belongs_to(:comment, Comment)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(event, attrs) do
|
||||
event
|
||||
|> cast(attrs, @attrs)
|
||||
# TODO: Enforce having either event_id or comment_id
|
||||
|> validate_required(@required_attrs)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new mention
|
||||
"""
|
||||
@spec create_mention(map()) :: {:ok, t} | {:error, Ecto.Changeset.t()}
|
||||
def create_mention(args) do
|
||||
with {:ok, %__MODULE__{} = mention} <-
|
||||
%__MODULE__{}
|
||||
|> changeset(args)
|
||||
|> Repo.insert() do
|
||||
{:ok, Repo.preload(mention, [:actor, :event, :comment])}
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user