228 lines
6.7 KiB
Elixir
228 lines
6.7 KiB
Elixir
defmodule BDS.UI.PostEditor.Metadata do
|
|
@moduledoc false
|
|
|
|
import Ecto.Query
|
|
|
|
alias BDS.{I18n, Media, Metadata, PostLinks, Posts, Preview, Repo, Templates}
|
|
alias BDS.Media.Media, as: MediaRecord
|
|
alias BDS.Posts.{Post, PostMedia}
|
|
use Gettext, backend: BDS.Gettext
|
|
|
|
@spec project_metadata(term()) :: term()
|
|
def project_metadata(nil), do: %{main_language: "en", blog_languages: []}
|
|
|
|
def project_metadata(project_id) do
|
|
{:ok, metadata} = Metadata.get_project_metadata(project_id)
|
|
metadata
|
|
rescue
|
|
_error -> %{main_language: "en", blog_languages: []}
|
|
end
|
|
|
|
@spec canonical_language(term(), term()) :: term()
|
|
def canonical_language(post, metadata) do
|
|
BDS.UI.PostEditor.Draft.normalize_language(
|
|
post.language,
|
|
metadata.main_language || "en"
|
|
)
|
|
end
|
|
|
|
@spec translations(term()) :: term()
|
|
def translations(post_id) do
|
|
{:ok, translations} = Posts.list_post_translations(post_id)
|
|
Map.new(translations, fn translation -> {translation.language, translation} end)
|
|
end
|
|
|
|
@spec languages(term()) :: term()
|
|
def languages(metadata) do
|
|
(([metadata.main_language || "en"] ++ (metadata.blog_languages || [])) ++
|
|
Enum.map(I18n.supported_languages(), & &1.code))
|
|
|> Enum.reject(&is_nil/1)
|
|
|> Enum.uniq()
|
|
end
|
|
|
|
@spec template_options(term()) :: term()
|
|
def template_options(project_id) do
|
|
Repo.all(
|
|
from template in Templates.Template,
|
|
where: template.project_id == ^project_id,
|
|
order_by: [asc: template.title, asc: template.slug],
|
|
select: %{
|
|
slug: template.slug,
|
|
title: fragment("COALESCE(?, ?)", template.title, template.slug)
|
|
}
|
|
)
|
|
rescue
|
|
_error -> []
|
|
end
|
|
|
|
@spec linked_media(term()) :: term()
|
|
def linked_media(post_id) do
|
|
rows =
|
|
Repo.all(
|
|
from pm in PostMedia,
|
|
where: pm.post_id == ^post_id,
|
|
order_by: [asc: pm.sort_order, asc: pm.media_id],
|
|
select: {pm.media_id, pm.sort_order}
|
|
)
|
|
|
|
Enum.map(rows, fn {media_id, sort_order} ->
|
|
case Media.get_media(media_id) do
|
|
%MediaRecord{} = media ->
|
|
%{
|
|
media_id: media.id,
|
|
has_thumbnail: String.starts_with?(to_string(media.mime_type || ""), "image/"),
|
|
name: media.title || media.original_name || media.id,
|
|
sort_order: sort_order || 0
|
|
}
|
|
|
|
_other ->
|
|
nil
|
|
end
|
|
end)
|
|
|> Enum.reject(&is_nil/1)
|
|
rescue
|
|
_error -> []
|
|
end
|
|
|
|
@spec post_links(term()) :: term()
|
|
def post_links(post_id) do
|
|
%{
|
|
backlinks: related_posts(PostLinks.list_incoming_links(post_id), :source_post_id),
|
|
outlinks: related_posts(PostLinks.list_outgoing_links(post_id), :target_post_id)
|
|
}
|
|
end
|
|
|
|
defp related_posts(links, key) do
|
|
Enum.map(links, fn link ->
|
|
case Posts.get_post(Map.fetch!(link, key)) do
|
|
%Post{} = post ->
|
|
%{
|
|
id: post.id,
|
|
title: post.title || post.slug || post.id,
|
|
text: link.link_text || post.slug || post.id
|
|
}
|
|
|
|
_other ->
|
|
nil
|
|
end
|
|
end)
|
|
|> Enum.reject(&is_nil/1)
|
|
end
|
|
|
|
@spec translation_flags(term(), term(), term(), term()) :: term()
|
|
def translation_flags(post, canonical_language, active_language, translations) do
|
|
canonical = %{
|
|
language: canonical_language,
|
|
flag: I18n.flag(canonical_language),
|
|
status: Atom.to_string(post.status || :draft),
|
|
active: active_language == canonical_language,
|
|
label: canonical_language
|
|
}
|
|
|
|
others =
|
|
translations
|
|
|> Map.values()
|
|
|> Enum.sort_by(& &1.language)
|
|
|> Enum.map(fn translation ->
|
|
%{
|
|
language: translation.language,
|
|
flag: I18n.flag(translation.language),
|
|
status: Atom.to_string(translation.status || :draft),
|
|
active: active_language == translation.language,
|
|
label: translation.language
|
|
}
|
|
end)
|
|
|
|
[canonical | others]
|
|
end
|
|
|
|
@spec footer(term(), term(), term(), term()) :: term()
|
|
def footer(post, translation, active_language, canonical_language) do
|
|
if active_language == canonical_language do
|
|
%{
|
|
created_at: format_timestamp(post.created_at),
|
|
updated_at: format_timestamp(post.updated_at),
|
|
published_at: format_timestamp(post.published_at)
|
|
}
|
|
else
|
|
%{
|
|
created_at: format_timestamp((translation && translation.created_at) || post.created_at),
|
|
updated_at: format_timestamp((translation && translation.updated_at) || post.updated_at),
|
|
published_at: format_timestamp(translation && translation.published_at)
|
|
}
|
|
end
|
|
end
|
|
|
|
defp format_timestamp(nil), do: ""
|
|
|
|
defp format_timestamp(timestamp) do
|
|
timestamp
|
|
|> DateTime.from_unix!(:millisecond)
|
|
|> Calendar.strftime("%x")
|
|
end
|
|
|
|
@spec display_title(term(), term(), term()) :: term()
|
|
def display_title(title, slug, fallback_id) do
|
|
blank_to_nil(title) || blank_to_nil(slug) || fallback_id || dgettext("ui", "Untitled")
|
|
end
|
|
|
|
@spec gallery_count(term()) :: term()
|
|
def gallery_count(form) do
|
|
content = form |> Map.get("content", "") |> to_string()
|
|
|
|
image_count =
|
|
content
|
|
|> then(&Regex.scan(~r/!\[[^\]]*\]\([^\)]+\)/, &1))
|
|
|> length()
|
|
|
|
gallery_macro_count =
|
|
content
|
|
|> then(&Regex.scan(~r/\[\[gallery\]\]/i, &1))
|
|
|> length()
|
|
|
|
max(image_count, gallery_macro_count)
|
|
end
|
|
|
|
@spec preview_url(term(), term(), term(), term()) :: term()
|
|
def preview_url(_post, _active_language, _canonical_language, mode) when mode != :preview,
|
|
do: nil
|
|
|
|
@spec preview_url(term(), term(), term(), term()) :: term()
|
|
def preview_url(%Post{} = post, active_language, canonical_language, :preview) do
|
|
query =
|
|
%{}
|
|
|> maybe_put_query("draft", "true")
|
|
|> maybe_put_query("post_id", post.id)
|
|
|> maybe_put_query("lang", active_language != canonical_language && active_language)
|
|
|
|
Preview.base_url() <>
|
|
canonical_preview_path(post.created_at, post.slug) <> "?" <> URI.encode_query(query)
|
|
end
|
|
|
|
defp canonical_preview_path(created_at_ms, slug) do
|
|
datetime = DateTime.from_unix!(created_at_ms, :millisecond)
|
|
"/#{datetime.year}/#{BDS.Strings.pad2(datetime.month)}/#{BDS.Strings.pad2(datetime.day)}/#{slug || ""}"
|
|
end
|
|
|
|
defp maybe_put_query(query, _key, false), do: query
|
|
defp maybe_put_query(query, _key, nil), do: query
|
|
defp maybe_put_query(query, key, value), do: Map.put(query, key, value)
|
|
|
|
@spec truthy?(term()) :: boolean()
|
|
def truthy?(value), do: BDS.Values.truthy?(value)
|
|
|
|
@spec blank?(term()) :: term()
|
|
def blank?(value), do: blank_to_nil(value) == nil
|
|
|
|
@spec blank_to_nil(term()) :: term()
|
|
def blank_to_nil(value) do
|
|
value
|
|
|> to_string()
|
|
|> String.trim()
|
|
|> case do
|
|
"" -> nil
|
|
trimmed -> trimmed
|
|
end
|
|
end
|
|
end
|