Current section

Files

Jump to
phoenix_kit_publishing lib phoenix_kit_publishing web editor helpers.ex
Raw

lib/phoenix_kit_publishing/web/editor/helpers.ex

defmodule PhoenixKit.Modules.Publishing.Web.Editor.Helpers do
@moduledoc """
Shared helper functions for the publishing editor.
Contains utilities for URL building, language handling,
virtual post creation, and other common operations.
"""
alias PhoenixKit.Modules.Publishing
alias PhoenixKit.Modules.Publishing.Constants
alias PhoenixKit.Modules.Publishing.LanguageHelpers
alias PhoenixKit.Modules.Publishing.Web.Editor.Translation
alias PhoenixKit.Modules.Publishing.Web.HTML, as: PublishingHTML
alias PhoenixKit.Modules.Storage.URLSigner
alias PhoenixKit.Utils.Routes
# ============================================================================
# Language Helpers
# ============================================================================
@doc """
Assigns current language with enabled/known status.
"""
def assign_current_language(socket, language_code) do
enabled_languages = socket.assigns[:all_enabled_languages] || []
lang_info = Publishing.get_language_info(language_code)
default_language = LanguageHelpers.get_primary_language()
# Get language names for display
current_language_name = if lang_info, do: lang_info.name, else: String.upcase(language_code)
default_language_name = get_language_name(default_language)
socket
|> Phoenix.Component.assign(:current_language, language_code)
|> Phoenix.Component.assign(:current_language_name, current_language_name)
|> Phoenix.Component.assign(:default_language, default_language)
|> Phoenix.Component.assign(:default_language_name, default_language_name)
|> Phoenix.Component.assign(
:current_language_enabled,
Publishing.language_enabled?(language_code, enabled_languages)
)
|> Phoenix.Component.assign(:current_language_known, lang_info != nil)
|> Translation.maybe_clear_completed_translation_status()
end
@doc """
Gets the language name for a language code.
"""
def get_language_name(language_code) do
case Publishing.get_language_info(language_code) do
%{name: name} -> name
_ -> String.upcase(language_code)
end
end
@doc """
Formats a list of language codes for display.
"""
def format_language_list(language_codes) when is_list(language_codes) do
count = length(language_codes)
cond do
count == 0 ->
""
count <= 3 ->
Enum.map_join(language_codes, ", ", &get_language_name/1)
true ->
"#{count} languages"
end
end
def format_language_list(_), do: ""
@doc """
Gets the editor language from assigns.
"""
def editor_language(assigns) do
assigns[:current_language] ||
assigns |> Map.get(:post, %{}) |> Map.get(:language) ||
hd(Publishing.enabled_language_codes())
end
@doc """
Builds language data for the publishing_language_switcher component.
"""
def build_editor_languages(post, enabled_languages, current_language) do
post_primary = LanguageHelpers.get_primary_language()
all_languages =
Publishing.order_languages_for_display(
post.available_languages || [],
enabled_languages,
post_primary
)
language_statuses = Map.get(post, :language_statuses) || %{}
Enum.map(all_languages, fn lang_code ->
lang_info = Publishing.get_language_info(lang_code)
content_exists = lang_code in (post.available_languages || [])
is_current = lang_code == current_language
is_enabled = Publishing.language_enabled?(lang_code, enabled_languages)
is_known = lang_info != nil
status = Map.get(language_statuses, lang_code)
display_code = Publishing.get_display_code(lang_code, enabled_languages)
%{
code: lang_code,
display_code: display_code,
name: if(lang_info, do: lang_info.name, else: lang_code),
flag: if(lang_info, do: lang_info.flag, else: ""),
status: status,
exists: content_exists,
is_current: is_current,
enabled: is_enabled,
known: is_known,
# is_default is used for ordering only, not for special UI treatment
is_default: lang_code == post_primary,
uuid: post[:uuid]
}
end)
end
# ============================================================================
# URL Helpers
# ============================================================================
@doc """
Builds the public URL for a post.
"""
def build_public_url(post, language) do
if Map.get(post.metadata, :status) == "published" do
build_url_for_mode(post, language)
else
nil
end
end
defp build_url_for_mode(post, language) do
group_slug = post.group || "group"
mode = Map.get(post, :mode)
cond do
Constants.slug_mode?(mode) -> build_slug_mode_url(group_slug, post, language)
Constants.timestamp_mode?(mode) -> build_timestamp_mode_url(group_slug, post, language)
true -> nil
end
end
defp build_slug_mode_url(group_slug, post, language) do
if post.slug do
PublishingHTML.build_post_url(group_slug, post, language)
else
nil
end
end
defp build_timestamp_mode_url(group_slug, post, language) do
if post.metadata.published_at do
case DateTime.from_iso8601(post.metadata.published_at) do
{:ok, _datetime, _} -> PublishingHTML.build_post_url(group_slug, post, language)
_ -> nil
end
else
nil
end
end
@doc """
Gets the URL for a media asset from storage.
"""
def get_file_url(file_uuid) do
URLSigner.signed_url(file_uuid, "original")
end
# ============================================================================
# Virtual Post Building
# ============================================================================
@doc """
Builds a virtual post for new post creation.
"""
def build_virtual_post(group_slug, "slug", primary_language, now) do
%{
group: group_slug,
date: nil,
time: nil,
metadata: %{
title: "",
status: "draft",
published_at: DateTime.to_iso8601(now),
slug: "",
featured_image_uuid: nil
},
content: "",
language: primary_language,
available_languages: [],
mode: :slug,
slug: nil
}
end
def build_virtual_post(group_slug, _mode, primary_language, now) do
date = DateTime.to_date(now)
time = DateTime.to_time(now)
%{
group: group_slug,
date: date,
time: time,
metadata: %{
title: "",
status: "draft",
published_at: DateTime.to_iso8601(now),
featured_image_uuid: nil
},
content: "",
language: primary_language,
available_languages: [],
mode: :timestamp
}
end
@doc """
Builds a virtual translation for a new language.
"""
def build_virtual_translation(post, group_slug, new_language, socket) do
post
|> Map.put(:language, new_language)
|> Map.put(:group, group_slug || "group")
|> Map.put(:content, "")
|> Map.put(:metadata, Map.put(post.metadata, :title, ""))
|> Map.put(:mode, post.mode)
|> Map.put(:slug, post.slug || Map.get(socket.assigns.form, "slug"))
end
# ============================================================================
# Featured Image Helpers
# ============================================================================
@doc """
Gets the preview URL for a featured image.
"""
def featured_image_preview_url(value) do
case sanitize_featured_image_uuid(value) do
nil ->
nil
file_uuid ->
PublishingHTML.featured_image_url(
%{metadata: %{featured_image_uuid: file_uuid}},
"medium"
)
end
end
@doc """
Sanitizes a featured image ID value.
"""
def sanitize_featured_image_uuid(value) when is_binary(value) do
value
|> String.trim()
|> case do
"" -> nil
trimmed -> trimmed
end
end
def sanitize_featured_image_uuid(_), do: nil
# ============================================================================
# URL Construction Helpers
# ============================================================================
@doc """
Builds the URL for a post overview page.
"""
def build_post_url(group_slug, post) do
Routes.path("/admin/publishing/#{group_slug}/#{require_uuid!(post)}")
end
@doc """
Builds the URL for the post editor.
Options: `:version`, `:lang`
"""
def build_edit_url(group_slug, post, opts \\ []) do
uuid = require_uuid!(post)
base = "/admin/publishing/#{group_slug}/#{uuid}/edit"
params = build_query_params(opts)
if params == "" do
Routes.path(base)
else
Routes.path("#{base}?#{params}")
end
end
@doc """
Builds the URL for the post preview.
"""
def build_preview_url(group_slug, post) do
Routes.path("/admin/publishing/#{group_slug}/#{require_uuid!(post)}/preview")
end
@doc """
Builds the URL for creating a new post.
"""
def build_new_post_url(group_slug) do
Routes.path("/admin/publishing/#{group_slug}/new")
end
defp build_query_params(opts) do
params =
[]
|> maybe_add_param("v", opts[:version])
|> maybe_add_param("lang", opts[:lang])
URI.encode_query(params)
end
defp maybe_add_param(params, _key, nil), do: params
defp maybe_add_param(params, key, value), do: [{key, value} | params]
defp require_uuid!(post) do
case post[:uuid] do
nil -> raise ArgumentError, "post UUID is required for URL construction, got nil"
uuid -> uuid
end
end
end