mobilizon/lib/mobilizon/discussions/discussion.ex

106 lines
3.1 KiB
Elixir

defmodule Mobilizon.Discussions.Discussion.TitleSlug do
@moduledoc """
Module to generate the slug for discussions
"""
use EctoAutoslugField.Slug, from: [:title, :id], to: :slug
@spec build_slug([String.t()], Ecto.Changeset.t()) :: String.t()
def build_slug([title, id], %Ecto.Changeset{valid?: true}) do
[title, ShortUUID.encode!(id)]
|> Enum.join("-")
|> Slugger.slugify()
end
def build_slug(_sources, %Ecto.Changeset{valid?: false}), do: ""
end
defmodule Mobilizon.Discussions.Discussion do
@moduledoc """
Represents a discussion
"""
use Ecto.Schema
import Ecto.Changeset
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.Comment
alias Mobilizon.Discussions.Discussion.TitleSlug
alias Mobilizon.Web.Endpoint
alias Mobilizon.Web.Router.Helpers, as: Routes
import Mobilizon.Web.Gettext, only: [dgettext: 2]
@type t :: %__MODULE__{
id: String.t(),
creator: Actor.t(),
actor: Actor.t(),
title: String.t(),
url: String.t(),
slug: String.t(),
last_comment: Comment.t(),
comments: list(Comment.t())
}
@required_attrs [:actor_id, :creator_id, :title, :last_comment_id, :url, :id]
@optional_attrs []
@attrs @required_attrs ++ @optional_attrs
@primary_key {:id, Ecto.UUID, autogenerate: true}
schema "discussions" do
field(:title, :string)
field(:slug, TitleSlug.Type)
field(:url, :string)
belongs_to(:creator, Actor)
belongs_to(:actor, Actor)
belongs_to(:last_comment, Comment)
has_many(:comments, Comment, foreign_key: :discussion_id)
timestamps(type: :utc_datetime)
end
@doc false
@spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = discussion, attrs) do
discussion
|> cast(attrs, @attrs)
|> maybe_generate_id()
|> validate_required([:title, :id], message: dgettext("errors", "can't be blank"))
|> TitleSlug.maybe_generate_slug()
|> TitleSlug.unique_constraint()
|> maybe_generate_url()
|> validate_required(@required_attrs)
end
defp maybe_generate_id(%Ecto.Changeset{} = changeset) do
case fetch_field(changeset, :id) do
res when res in [:error, {:data, nil}] ->
put_change(changeset, :id, Ecto.UUID.generate())
_ ->
changeset
end
end
@spec maybe_generate_url(Ecto.Changeset.t()) :: Ecto.Changeset.t()
defp maybe_generate_url(%Ecto.Changeset{} = changeset) do
with res when res in [:error, {:data, nil}] <- fetch_field(changeset, :url),
{changes, slug} when changes in [:changes, :data] <-
fetch_field(changeset, :slug),
{_changes, actor_id} <-
fetch_field(changeset, :actor_id),
%Actor{preferred_username: preferred_username} <-
Actors.get_actor(actor_id),
url <- generate_url(preferred_username, slug) do
put_change(changeset, :url, url)
else
_ -> changeset
end
end
@spec generate_url(String.t(), String.t()) :: String.t()
defp generate_url(preferred_username, slug),
do: Routes.page_url(Endpoint, :discussion, preferred_username, slug)
end