Packages
phoenix_kit
1.7.21
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/preview.ex
defmodule PhoenixKit.Modules.Publishing.Web.Preview do
@moduledoc """
Preview rendering for .phk publishing posts.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
require Logger
alias Phoenix.HTML
alias PhoenixKit.Modules.Publishing
# alias PhoenixKit.Modules.Publishing.PageBuilder # COMMENTED OUT: Component system
alias PhoenixKit.Modules.Publishing.Renderer
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
@impl true
def mount(params, _session, socket) do
blog_slug = params["blog"] || params["category"] || params["type"]
socket =
socket
|> assign(:project_title, Settings.get_setting("project_title", "PhoenixKit"))
|> assign(:page_title, "Preview")
|> assign(:blog_slug, blog_slug)
|> assign(:blog_name, Publishing.group_name(blog_slug) || blog_slug)
|> assign(
:current_path,
Routes.path("/admin/publishing/#{blog_slug}/preview",
locale: socket.assigns.current_locale_base
)
)
|> assign(:rendered_content, nil)
|> assign(:error, nil)
|> assign(:preview_source, :saved)
{:ok, socket}
end
@impl true
def handle_params(params, _uri, socket) do
cond do
Map.has_key?(params, "preview_token") ->
handle_preview_token(params, socket)
Map.has_key?(params, "path") ->
handle_saved_preview(params, socket)
true ->
{:noreply, socket}
end
end
defp handle_preview_token(%{"preview_token" => token} = params, socket) do
endpoint = socket.endpoint || PhoenixKitWeb.Endpoint
case Phoenix.Token.verify(endpoint, "blog-preview", token, max_age: 300) do
{:ok, data} ->
post =
build_preview_post(data, socket.assigns.blog_slug, socket.assigns.current_locale_base)
case render_markdown_content(data[:content] || "") do
{:ok, rendered_html} ->
{:noreply,
socket
|> assign(:post, post)
|> assign(:blog_slug, post.group)
|> assign(:blog_name, Publishing.group_name(post.group) || post.group)
|> assign(:rendered_content, rendered_html)
|> assign(:preview_token, token)
|> assign(:preview_data, data)
|> assign(:error, nil)
|> assign(:preview_source, :unsaved)}
{:error, error_message} ->
{:noreply,
socket
|> assign(:post, post)
|> assign(:blog_slug, post.group)
|> assign(:blog_name, Publishing.group_name(post.group) || post.group)
|> assign(:rendered_content, nil)
|> assign(:preview_token, token)
|> assign(:preview_data, data)
|> assign(:error, error_message)
|> assign(:preview_source, :unsaved)}
end
{:error, _reason} ->
params
|> Map.delete("preview_token")
|> handle_saved_preview(socket)
end
end
defp handle_saved_preview(%{"path" => path}, socket) do
blog_slug = socket.assigns.blog_slug
case Publishing.read_post(blog_slug, path) do
{:ok, post} ->
case render_markdown_content(post.content) do
{:ok, rendered_html} ->
{:noreply,
socket
|> assign(:post, post)
|> assign(:rendered_content, rendered_html)
|> assign(:preview_token, nil)
|> assign(:preview_data, nil)
|> assign(:error, nil)
|> assign(:preview_source, :saved)}
{:error, error_message} ->
{:noreply,
socket
|> assign(:post, post)
|> assign(:rendered_content, nil)
|> assign(:preview_token, nil)
|> assign(:preview_data, nil)
|> assign(:error, error_message)
|> assign(:preview_source, :saved)}
end
{:error, _reason} ->
{:noreply,
socket
|> put_flash(:error, gettext("Post not found"))
|> push_navigate(
to:
Routes.path("/admin/publishing/#{blog_slug}",
locale: socket.assigns.current_locale_base
)
)}
end
end
defp handle_saved_preview(_params, socket), do: {:noreply, socket}
@impl true
def handle_event("back_to_editor", _params, socket) do
query = socket |> preview_return_params() |> encode_query()
destination =
Routes.path(
"/admin/publishing/#{socket.assigns.blog_slug}/edit#{query}",
locale: socket.assigns.current_locale_base
)
{:noreply, push_navigate(socket, to: destination)}
end
# ============================================================================
# COMMENTED OUT: Component-based rendering system - Preview assigns builder
# ============================================================================
# This was used to build sample data for the component rendering system.
# Related to: lib/modules/publishing/page_builder.ex
# ============================================================================
defp render_markdown_content(content) when is_binary(content) do
content
|> Renderer.render_markdown()
|> HTML.raw()
|> then(&{:ok, &1})
rescue
error ->
Logger.error("Preview markdown rendering failed", error: inspect(error))
{:error, gettext("Failed to render markdown preview.")}
end
defp build_preview_post(data, fallback_blog_slug, fallback_locale) do
blog_slug = data[:blog_slug] || fallback_blog_slug
language = data[:language] || fallback_locale
mode = data[:mode] || :timestamp
metadata = extract_preview_metadata(data[:metadata] || %{})
path = resolve_preview_path(data[:path], blog_slug, metadata[:slug], language, mode)
available_languages = data[:available_languages] || []
available_languages =
[language | available_languages] |> Enum.reject(&is_nil/1) |> Enum.uniq()
%{
group: blog_slug,
slug: metadata[:slug],
date: nil,
time: nil,
path: path,
full_path: nil,
metadata: metadata,
content: data[:content] || "",
language: language,
available_languages: available_languages,
mode: mode
}
end
defp extract_preview_metadata(raw_metadata) do
Enum.reduce(raw_metadata, %{title: "", status: "draft", published_at: nil, slug: nil}, fn
{key, value}, acc when key in [:title, :status, :published_at, :slug] ->
Map.put(acc, key, value)
{"title", value}, acc ->
Map.put(acc, :title, value)
{"status", value}, acc ->
Map.put(acc, :status, value)
{"published_at", value}, acc ->
Map.put(acc, :published_at, value)
{"slug", value}, acc ->
Map.put(acc, :slug, value)
_, acc ->
acc
end)
end
defp resolve_preview_path(path, _blog_slug, _slug, _language, _mode)
when is_binary(path) and path != "" do
path
end
defp resolve_preview_path(_path, blog_slug, slug, language, :slug)
when is_binary(slug) and slug != "" do
Path.join([blog_slug, slug, "#{language}.phk"])
end
defp resolve_preview_path(_, _blog_slug, _slug, _language, _mode), do: nil
defp preview_return_params(socket) do
base =
%{}
|> maybe_put_path_param(socket.assigns.post.path)
base
|> maybe_put_preview_token(socket.assigns[:preview_token])
|> maybe_put_new_flag(socket.assigns[:preview_data])
end
defp maybe_put_path_param(params, path) when is_binary(path) and path != "" do
Map.put(params, "path", path)
end
defp maybe_put_path_param(params, _), do: params
defp maybe_put_preview_token(params, token) when is_binary(token) and token != "" do
Map.put(params, "preview_token", token)
end
defp maybe_put_preview_token(params, _), do: params
defp maybe_put_new_flag(params, %{is_new_post: true}), do: Map.put(params, "new", "true")
defp maybe_put_new_flag(params, _), do: params
defp encode_query(params) do
case URI.encode_query(params) do
"" -> ""
encoded -> "?" <> encoded
end
end
end