Packages
phoenix_kit
1.6.9
1.7.211
1.7.210
1.7.209
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/live/modules/blogging/blog.ex
defmodule PhoenixKitWeb.Live.Modules.Blogging.Blog do
@moduledoc """
Lists posts for a blog and provides creation actions.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Blogging.Renderer
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Date, as: UtilsDate
alias PhoenixKit.Utils.Routes
alias PhoenixKitWeb.BlogHTML
alias PhoenixKitWeb.Live.Modules.Blogging
alias PhoenixKitWeb.Live.Modules.Blogging.Storage
@impl true
def mount(params, _session, socket) do
blog_slug = params["blog"] || params["category"] || params["type"]
locale = params["locale"] || socket.assigns[:current_locale] || "en"
Gettext.put_locale(PhoenixKitWeb.Gettext, locale)
Process.put(:phoenix_kit_current_locale, locale)
# Load date/time format settings once for performance
date_time_settings =
Settings.get_settings_cached(
["date_format", "time_format", "time_zone"],
%{
"date_format" => "Y-m-d",
"time_format" => "H:i",
"time_zone" => "0"
}
)
blogs = Blogging.list_blogs()
current_blog = Enum.find(blogs, fn blog -> blog["slug"] == blog_slug end)
posts = if blog_slug, do: Blogging.list_posts(blog_slug, locale), else: []
current_path =
case blog_slug do
nil -> Routes.path("/admin/blogging", locale: locale)
slug -> Routes.path("/admin/blogging/#{slug}", locale: locale)
end
socket =
socket
|> assign(:current_locale, locale)
|> assign(:project_title, Settings.get_setting("project_title", "PhoenixKit"))
|> assign(:page_title, "Blogging")
|> assign(:current_path, current_path)
|> assign(:blogs, blogs)
|> assign(:current_blog, current_blog)
|> assign(:blog_slug, blog_slug)
|> assign(:enabled_languages, Storage.enabled_language_codes())
|> assign(:posts, posts)
|> assign(:endpoint_url, nil)
|> assign(:date_time_settings, date_time_settings)
{:ok, redirect_if_missing(socket)}
end
@impl true
def handle_params(_params, uri, socket) do
posts =
case socket.assigns.blog_slug do
nil -> []
slug -> Blogging.list_posts(slug, socket.assigns.current_locale)
end
endpoint_url = extract_endpoint_url(uri)
socket =
socket
|> assign(:posts, posts)
|> assign(:endpoint_url, endpoint_url)
{:noreply, redirect_if_missing(socket)}
end
@impl true
def handle_event("create_post", _params, %{assigns: %{blog_slug: blog_slug}} = socket) do
{:noreply,
push_navigate(socket,
to:
Routes.path(
"/admin/blogging/#{blog_slug}/edit?new=true",
locale: socket.assigns.current_locale
)
)}
end
def handle_event("refresh", _params, socket) do
{:noreply,
assign(
socket,
:posts,
Blogging.list_posts(socket.assigns.blog_slug, socket.assigns.current_locale)
)}
end
def handle_event("add_language", %{"path" => post_path, "language" => lang_code}, socket) do
{:noreply,
push_navigate(socket,
to:
Routes.path(
"/admin/blogging/#{socket.assigns.blog_slug}/edit?path=#{URI.encode(post_path)}&switch_to=#{lang_code}",
locale: socket.assigns.current_locale
)
)}
end
def handle_event("change_status", %{"path" => post_path, "status" => new_status}, socket) do
scope = socket.assigns[:phoenix_kit_current_scope]
case Blogging.read_post(socket.assigns.blog_slug, post_path) do
{:ok, post} ->
case Blogging.update_post(socket.assigns.blog_slug, post, %{"status" => new_status}, %{
scope: scope
}) do
{:ok, updated_post} ->
# Invalidate cache for this post
invalidate_post_cache(socket.assigns.blog_slug, updated_post)
{:noreply,
socket
|> put_flash(:info, gettext("Status updated to %{status}", status: new_status))
|> assign(
:posts,
Blogging.list_posts(socket.assigns.blog_slug, socket.assigns.current_locale)
)}
{:error, _reason} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update status"))}
end
{:error, _reason} ->
{:noreply, put_flash(socket, :error, gettext("Post not found"))}
end
end
def handle_event(
"toggle_status",
%{"path" => post_path, "current-status" => current_status},
socket
) do
scope = socket.assigns[:phoenix_kit_current_scope]
new_status =
case current_status do
"draft" -> "published"
"published" -> "archived"
"archived" -> "draft"
_ -> "draft"
end
case Blogging.read_post(socket.assigns.blog_slug, post_path) do
{:ok, post} ->
case Blogging.update_post(socket.assigns.blog_slug, post, %{"status" => new_status}, %{
scope: scope
}) do
{:ok, _updated_post} ->
{:noreply,
socket
|> put_flash(:info, gettext("Status updated to %{status}", status: new_status))
|> assign(
:posts,
Blogging.list_posts(socket.assigns.blog_slug, socket.assigns.current_locale)
)}
{:error, _reason} ->
{:noreply, put_flash(socket, :error, gettext("Failed to update status"))}
end
{:error, _reason} ->
{:noreply, put_flash(socket, :error, gettext("Post not found"))}
end
end
defp redirect_if_missing(%{assigns: %{current_blog: nil}} = socket) do
case socket.assigns.blogs do
[%{"slug" => slug} | _] ->
push_navigate(socket,
to: Routes.path("/admin/blogging/#{slug}", locale: socket.assigns.current_locale)
)
[] ->
push_navigate(socket,
to: Routes.path("/admin/settings/blogging", locale: socket.assigns.current_locale)
)
end
end
defp redirect_if_missing(socket), do: socket
def format_datetime(
%{date: %Date{} = date, time: %Time{} = time},
current_user,
date_time_settings
) do
# Fallback to dummy user if current_user is nil
user = current_user || %{user_timezone: nil}
# Dates and times are already in the timezone they were created in
# Just format them with user preferences
date_str = UtilsDate.format_date_with_user_timezone_cached(date, user, date_time_settings)
time_str = UtilsDate.format_time_with_user_timezone_cached(time, user, date_time_settings)
"#{date_str} #{gettext("at")} #{time_str}"
end
def format_datetime(
%{metadata: %{published_at: published_at}},
current_user,
date_time_settings
)
when is_binary(published_at) do
# Fallback to dummy user if current_user is nil
user = current_user || %{user_timezone: nil}
case DateTime.from_iso8601(published_at) do
{:ok, dt, _} ->
# Convert DateTime to NaiveDateTime (assuming stored as UTC)
naive_dt = DateTime.to_naive(dt)
# Format date part with timezone conversion
date_str =
UtilsDate.format_date_with_user_timezone_cached(naive_dt, user, date_time_settings)
# Format time part with timezone conversion
time_str =
UtilsDate.format_time_with_user_timezone_cached(naive_dt, user, date_time_settings)
"#{date_str} #{gettext("at")} #{time_str}"
_ ->
gettext("Unsaved draft")
end
end
def format_datetime(_post, _user, _settings), do: gettext("Unsaved draft")
defp extract_endpoint_url(uri) when is_binary(uri) do
case URI.parse(uri) do
%URI{scheme: scheme, host: host, port: port} when not is_nil(scheme) and not is_nil(host) ->
port_string = if port in [80, 443], do: "", else: ":#{port}"
"#{scheme}://#{host}#{port_string}"
_ ->
""
end
end
defp extract_endpoint_url(_), do: ""
defp invalidate_post_cache(blog_slug, post) do
# Determine identifier based on post mode
identifier =
case post.mode do
:slug -> post.slug
_ -> post.path
end
# Invalidate the render cache for this post
Renderer.invalidate_cache(blog_slug, identifier, post.language)
end
end