Packages
phoenix_kit
1.7.79
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/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.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)
post_primary = socket.assigns[:post] && socket.assigns.post[:primary_language]
primary_language = post_primary || Publishing.get_primary_language()
is_primary = language_code == primary_language
# Check if this post's primary language matches the global setting
global_primary = Publishing.get_primary_language()
primary_lang_status =
cond do
post_primary == nil -> {:needs_update, :backfill}
post_primary != global_primary -> {:needs_update, :migration}
true -> {:ok, :current}
end
# Get language names for display
current_language_name = if lang_info, do: lang_info.name, else: String.upcase(language_code)
primary_language_name = get_language_name(primary_language)
global_primary_language_name = get_language_name(global_primary)
socket
|> Phoenix.Component.assign(:current_language, language_code)
|> Phoenix.Component.assign(:current_language_name, current_language_name)
|> Phoenix.Component.assign(:primary_language_name, primary_language_name)
|> Phoenix.Component.assign(:global_primary_language_name, global_primary_language_name)
|> Phoenix.Component.assign(
:current_language_enabled,
Publishing.language_enabled?(language_code, enabled_languages)
)
|> Phoenix.Component.assign(:current_language_known, lang_info != nil)
|> Phoenix.Component.assign(:is_primary_language, is_primary)
|> Phoenix.Component.assign(:post_primary_language_status, primary_lang_status)
|> 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 = post[:primary_language] || Publishing.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
is_primary = lang_code == post_primary
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_primary: is_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