Packages
phoenix_kit
1.6.14
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/phoenix_kit_web/controllers/blog_html.ex
defmodule PhoenixKitWeb.BlogHTML do
@moduledoc """
HTML rendering functions for BlogController.
"""
use PhoenixKitWeb, :html
alias PhoenixKit.Blogging.Renderer
alias PhoenixKit.Config
alias PhoenixKit.Storage
embed_templates("blog_html/*")
@doc """
Builds the public URL for a blog listing page.
Default language gets clean URLs without locale prefix.
"""
def blog_listing_path(language, blog_slug, params \\ []) do
segments =
if default_language?(language), do: [blog_slug], else: [language, blog_slug]
base_path = build_public_path(segments)
case params do
[] -> base_path
_ -> base_path <> "?" <> URI.encode_query(params)
end
end
@doc """
Builds a post URL based on mode.
Default language gets clean URLs without locale prefix.
"""
def build_post_url(blog_slug, post, language) do
case post.mode do
:slug ->
segments =
if default_language?(language),
do: [blog_slug, post.slug],
else: [language, blog_slug, post.slug]
build_public_path(segments)
:timestamp ->
date = format_date_for_url(post.metadata.published_at)
time = format_time_for_url(post.metadata.published_at)
segments =
if default_language?(language),
do: [blog_slug, date, time],
else: [language, blog_slug, date, time]
build_public_path(segments)
_ ->
segments =
if default_language?(language),
do: [blog_slug, post.slug],
else: [language, blog_slug, post.slug]
build_public_path(segments)
end
end
@doc """
Formats a date for display.
"""
def format_date(datetime) when is_struct(datetime, DateTime) do
datetime
|> DateTime.to_date()
|> Calendar.strftime("%B %d, %Y")
end
def format_date(datetime_string) when is_binary(datetime_string) do
case DateTime.from_iso8601(datetime_string) do
{:ok, datetime, _} ->
datetime
|> DateTime.to_date()
|> Calendar.strftime("%B %d, %Y")
_ ->
datetime_string
end
end
def format_date(_), do: ""
@doc """
Formats a date for URL.
"""
def format_date_for_url(datetime) when is_struct(datetime, DateTime) do
datetime
|> DateTime.to_date()
|> Date.to_iso8601()
end
def format_date_for_url(_), do: "2025-01-01"
@doc """
Formats time for URL (HH:MM).
"""
def format_time_for_url(datetime) when is_struct(datetime, DateTime) do
datetime
|> DateTime.to_time()
|> Time.truncate(:second)
|> Time.to_string()
|> String.slice(0..4)
end
def format_time_for_url(_), do: "00:00"
@doc """
Pluralizes a word based on count.
"""
def pluralize(1, singular, _plural), do: "1 #{singular}"
def pluralize(count, _singular, plural), do: "#{count} #{plural}"
@doc """
Extracts and renders an excerpt from post content.
Returns content before <!-- more --> tag, or first paragraph if no tag.
Renders markdown and strips HTML tags for plain text display.
"""
def extract_excerpt(content) when is_binary(content) do
excerpt_markdown =
if String.contains?(content, "<!-- more -->") do
# Extract content before <!-- more --> tag
content
|> String.split("<!-- more -->")
|> List.first()
|> String.trim()
else
# Get first paragraph (content before first double newline)
content
|> String.split(~r/\n\s*\n/, parts: 2)
|> List.first()
|> String.trim()
end
# Render markdown to HTML
html = Renderer.render_markdown(excerpt_markdown)
# Strip HTML tags to get plain text
html
|> Phoenix.HTML.raw()
|> Phoenix.HTML.safe_to_string()
|> strip_html_tags()
|> String.trim()
end
def extract_excerpt(_), do: ""
defp strip_html_tags(html) when is_binary(html) do
html
|> String.replace(~r/<[^>]*>/, " ")
|> String.replace(~r/\s+/, " ")
|> String.trim()
end
defp build_public_path(segments) do
parts =
url_prefix_segments() ++
(segments
|> Enum.reject(&(&1 in [nil, ""]))
|> Enum.map(&to_string/1))
case parts do
[] -> "/"
_ -> "/" <> Enum.join(parts, "/")
end
end
defp url_prefix_segments do
Config.get_url_prefix()
|> case do
"/" -> []
prefix -> prefix |> String.trim("/") |> String.split("/", trim: true)
end
end
# Check if the given language is the default language (first admin language)
# Default language gets clean URLs without locale prefix
defp default_language?(language) do
default = get_default_admin_language()
language == default
end
# Get the default admin language base code (first in admin_languages list, or "en")
# Extracts base code from full dialect (e.g., "en-US" -> "en")
defp get_default_admin_language do
alias PhoenixKit.Modules.Languages.DialectMapper
case PhoenixKit.Settings.get_setting("admin_languages") do
nil ->
# No setting exists, default is "en"
"en"
json when is_binary(json) ->
case Jason.decode(json) do
# Extract base code from full dialect (e.g., "en-US" -> "en")
{:ok, [first | _]} -> DialectMapper.extract_base(first)
_ -> "en"
end
end
end
@doc """
Resolves a featured image URL for a post, falling back to the original variant.
"""
def featured_image_url(post, variant \\ "medium") do
post.metadata
|> Map.get(:featured_image_id)
|> resolve_featured_image_url(variant)
end
defp resolve_featured_image_url(nil, _variant), do: nil
defp resolve_featured_image_url("", _variant), do: nil
defp resolve_featured_image_url(file_id, variant) when is_binary(file_id) do
Storage.get_public_url_by_id(file_id, variant) ||
Storage.get_public_url_by_id(file_id)
rescue
_ -> nil
end
end