Packages
literature
0.2.0
0.5.1
0.5.0
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
retired
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.32
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A simple CMS / Blog
Current section
Files
Jump to
Current section
Files
lib/literature/post.ex
defmodule Literature.Post do
@moduledoc """
Literature Post Model
"""
use Literature.Web, :model
defmodule Locale do
@moduledoc """
Post languages
"""
use Literature.Web, :model
@embed_fields ~w(locale url)a
@primary_key false
embedded_schema do
field(:locale, :string)
field(:url, :string)
end
def changeset(%__MODULE__{} = struct, attrs \\ %{}) do
struct
|> cast(attrs, @embed_fields)
|> validate_required(@embed_fields)
end
end
schema "literature_posts" do
field(:slug, :string)
field(:title, :string)
field(:feature_image, Uploaders.Type)
field(:feature_image_alt, :string)
field(:feature_image_caption, :string)
field(:featured, :boolean)
field(:published_at, :utc_datetime)
field(:excerpt, :string)
field(:editor_json, :string)
field(:html, {:array, :string})
field(:meta_title, :string)
field(:meta_description, :string)
field(:meta_keywords, :string)
field(:og_image, Uploaders.Type)
field(:og_title, :string)
field(:og_description, :string)
field(:twitter_image, Uploaders.Type)
field(:twitter_title, :string)
field(:twitter_description, :string)
field(:status, :string, virtual: true)
field(:authors_ids, {:array, :string}, virtual: true)
field(:tags_ids, {:array, :string}, virtual: true)
field(:upload_image, Uploaders.Type, virtual: true)
field(:prev_post, :map, virtual: true)
field(:next_post, :map, virtual: true)
field(:similar_posts, {:array, :map}, virtual: true)
field(:custom_position, :integer, virtual: true)
belongs_to(:publication, Publication)
many_to_many(:authors, Author, join_through: "literature_authors_posts", on_replace: :delete)
many_to_many(:tags, Tag, join_through: "literature_tags_posts", on_replace: :delete)
embeds_many(:locales, Locale, on_replace: :delete)
timestamps()
end
@required_params ~w(
publication_id
slug
title
)a
@optional_params ~w(
feature_image_alt
feature_image_caption
featured
excerpt
editor_json
html
published_at
meta_title
meta_description
meta_keywords
og_title
og_description
twitter_title
twitter_description
)a
@attachments ~w(
feature_image
og_image
upload_image
twitter_image
)a
@doc false
def changeset(post, params) do
post
|> maybe_generate_id()
|> cast(params, @required_params ++ @optional_params)
|> maybe_generate_slug(post)
|> cast_attachments(params, @attachments)
|> cast_embed(:locales,
sort_param: :locales_order,
drop_param: :locales_delete
)
|> validate_required(@required_params, message: "This field is required")
|> unique_constraint(:slug,
name: :literature_posts_publication_id_slug_index,
message: "#{params["slug"]} slug is duplicated}"
)
|> put_assocs(params)
end
defp maybe_generate_slug(%{changes: %{title: title, slug: slug}} = changeset, _post)
when title != slug,
do: changeset
defp maybe_generate_slug(changeset, %{title: title, slug: slug}) when title != slug,
do: changeset
defp maybe_generate_slug(changeset, %{slug: nil}),
do: slugify(changeset, :title)
defp maybe_generate_slug(changeset, _), do: changeset
def resolve(post) when is_struct(post) do
%{
post
| status: get_status(post),
authors_ids: Enum.map(post.authors, & &1.id),
tags_ids: Enum.map(post.tags, & &1.id)
}
end
def resolve(post), do: post
def resolve_prev_and_next_post(post, %{published_posts: published_posts}) do
post_index = Enum.find_index(published_posts, &(&1.id == post.id))
post
|> build_post(:prev, published_posts, post_index)
|> build_post(:next, published_posts, post_index)
end
def resolve_similar_posts(%{tags_ids: tags_ids} = post, %{published_posts: published_posts}) do
similar_posts =
published_posts
|> Stream.reject(&(&1.id == post.id))
|> Stream.map(&resolve/1)
|> Stream.filter(&List.myers_difference(&1.tags_ids, tags_ids)[:eq])
|> Stream.take(3)
|> Enum.to_list()
%{post | similar_posts: similar_posts}
end
defp build_post(post, :prev, _, nil), do: post
defp build_post(post, :prev, published_posts, index),
do: %{post | prev_post: Enum.fetch!(published_posts, index - 1)}
defp build_post(post, :next, published_posts, index) do
%{post | next_post: Enum.fetch!(published_posts, index + 1)}
rescue
_ -> post
end
defp get_status(%{published_at: published_at}) do
datetime = Timex.now() |> Timex.local()
cond do
is_nil(published_at) -> "draft"
Timex.compare(published_at, datetime) < 1 -> "published"
Timex.compare(published_at, datetime) == 1 -> "scheduled"
end
end
defp put_assocs(changeset, %{"authors_ids" => ""}),
do: add_error(changeset, :authors_ids, "Required at least one author")
defp put_assocs(changeset, %{"tags_ids" => ""}),
do: add_error(changeset, :tags_ids, "Required at least one tag")
defp put_assocs(%{valid?: true} = changeset, %{"authors_ids" => authors, "tags_ids" => tags}) do
changeset
|> put_assoc(:authors, Enum.map(authors, &Literature.get_author!/1))
|> put_assoc(:tags, Enum.map(tags, &Literature.get_tag!/1))
end
defp put_assocs(changeset, _), do: changeset
end