Packages

phoenix_kit

1.7.27
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
phoenix_kit lib modules sitemap web settings.ex
Raw

lib/modules/sitemap/web/settings.ex

defmodule PhoenixKit.Modules.Sitemap.Web.Settings do
@moduledoc """
LiveView for sitemap configuration and management.
Provides admin interface for:
- Enabling/disabling sitemap module
- Configuring data sources (entities, blogs, pages, static)
- Setting HTML sitemap style
- Manual sitemap regeneration
- Viewing generation statistics
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Sitemap
alias PhoenixKit.Modules.Sitemap.Generator
alias PhoenixKit.Modules.Sitemap.SchedulerWorker
alias PhoenixKit.PubSub.Manager, as: PubSubManager
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Date, as: UtilsDate
alias PhoenixKit.Utils.Routes
@impl true
def mount(params, _session, socket) do
# Subscribe to sitemap updates for real-time UI refresh
if connected?(socket) do
PubSubManager.subscribe("sitemap:updates")
PubSubManager.subscribe("sitemap:settings")
end
locale = params["locale"] || "en"
project_title = Settings.get_project_title()
site_url = Settings.get_setting("site_url", "")
config = Sitemap.get_config()
# Generate sitemap version from last_generated or current time
sitemap_version = get_sitemap_version(config)
socket =
socket
|> assign(:page_title, "Sitemap Settings")
|> assign(:project_title, project_title)
|> assign(:current_locale, locale)
|> assign(:current_path, Routes.path("/admin/settings/sitemap", locale: locale))
|> assign(:config, config)
|> assign(:site_url, site_url)
|> assign(:generating, false)
|> assign(:preview_mode, nil)
|> assign(:preview_content, nil)
|> assign(:show_preview, false)
|> assign(:sitemap_version, sitemap_version)
{:ok, socket}
end
@impl true
def handle_params(_params, _url, socket) do
{:noreply, socket}
end
@impl true
def handle_event("toggle_sitemap", _params, socket) do
new_enabled = !socket.assigns.config.enabled
result =
if new_enabled do
Sitemap.enable_system()
else
Sitemap.disable_system()
end
case result do
{:ok, _} ->
config = Sitemap.get_config()
message = if new_enabled, do: "Sitemap enabled", else: "Sitemap disabled"
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast to all admin sessions
broadcast_settings_change(:sitemap_toggled, %{enabled: new_enabled, version: new_version})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)
|> put_flash(:info, message)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update sitemap status")}
end
end
@impl true
def handle_event("toggle_source", %{"source" => source}, socket) do
# Router discovery uses different key pattern
key =
if source == "router_discovery" do
"sitemap_router_discovery_enabled"
else
"sitemap_include_#{source}"
end
current = Settings.get_boolean_setting(key, true)
case Settings.update_boolean_setting(key, !current) do
{:ok, _} ->
# Invalidate cache when sources change
Generator.invalidate_cache()
config = Sitemap.get_config()
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast to all admin sessions
broadcast_settings_change(:source_changed, %{source: source, version: new_version})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update source setting")}
end
end
@impl true
def handle_event("toggle_html", _params, socket) do
current = socket.assigns.config.html_enabled
case Settings.update_boolean_setting("sitemap_html_enabled", !current) do
{:ok, _} ->
# Invalidate cache when XSL enabled/disabled
Generator.invalidate_cache()
config = Sitemap.get_config()
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast to all admin sessions
broadcast_settings_change(:html_changed, %{enabled: !current, version: new_version})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update HTML sitemap setting")}
end
end
@impl true
def handle_event("toggle_schedule", _params, socket) do
alias PhoenixKit.Modules.Sitemap.SchedulerWorker
current = socket.assigns.config.schedule_enabled
case Settings.update_boolean_setting("sitemap_schedule_enabled", !current) do
{:ok, _} ->
# Cancel scheduled jobs when disabling
unless current == false do
SchedulerWorker.cancel_scheduled()
end
config = Sitemap.get_config()
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast to all admin sessions
broadcast_settings_change(:schedule_changed, %{enabled: !current, version: new_version})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update schedule setting")}
end
end
@impl true
def handle_event("update_style", %{"style" => style}, socket) do
case Settings.update_setting("sitemap_html_style", style) do
{:ok, _} ->
# Invalidate cache so sitemap.xml regenerates with new XSL style
Generator.invalidate_cache()
config = Sitemap.get_config()
# Update version to force iframe reload
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast style change to all admin sessions
broadcast_settings_change(:style_changed, %{style: style, version: new_version})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)
|> put_flash(:info, "Style updated. Sitemap will use new style on next load.")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update style")}
end
end
@impl true
def handle_event("update_interval", %{"interval" => interval_str}, socket) do
case Integer.parse(interval_str) do
{interval, _} when interval > 0 ->
case Settings.update_setting("sitemap_schedule_interval_hours", interval_str) do
{:ok, _} ->
config = Sitemap.get_config()
new_version = DateTime.utc_now() |> DateTime.to_unix()
# Broadcast to all admin sessions
broadcast_settings_change(:interval_changed, %{
interval: interval,
version: new_version
})
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Failed to update interval")}
end
_ ->
{:noreply, put_flash(socket, :error, "Invalid interval value")}
end
end
@impl true
def handle_event("regenerate", _params, socket) do
# Get base URL for generation
base_url = Sitemap.get_base_url()
if base_url != "" do
# Use Oban worker for async generation (non-blocking UI)
case SchedulerWorker.regenerate_now() do
{:ok, _job} ->
{:noreply,
socket
|> assign(:generating, true)
|> put_flash(:info, "Sitemap generation queued. Stats will update automatically.")}
{:error, reason} ->
{:noreply,
socket
|> put_flash(:error, "Failed to queue generation: #{inspect(reason)}")}
end
else
{:noreply,
socket
|> put_flash(:error, "Please configure Base URL before generating")}
end
end
@impl true
def handle_event("preview", %{"type" => _type}, socket) do
base_url = Sitemap.get_base_url()
xsl_style = get_xsl_style(socket.assigns.config.html_style)
opts = [
base_url: base_url,
cache: false,
xsl_style: xsl_style,
xsl_enabled: socket.assigns.config.html_enabled
]
content =
case Generator.generate_xml(opts) do
{:ok, xml} -> xml
{:ok, xml, _parts} -> xml
_ -> "Error generating XML preview"
end
{:noreply,
socket
|> assign(:preview_mode, "xml")
|> assign(:preview_content, content)
|> assign(:show_preview, true)}
end
@impl true
def handle_event("close_preview", _params, socket) do
{:noreply,
socket
|> assign(:show_preview, false)
|> assign(:preview_mode, nil)
|> assign(:preview_content, nil)}
end
@impl true
def handle_event("invalidate_cache", _params, socket) do
# Clear cache AND trigger async regeneration via Oban
case Generator.invalidate_and_regenerate() do
{:ok, _job} ->
# Update version to force iframe reload after cache clear
new_version = DateTime.utc_now() |> DateTime.to_unix()
{:noreply,
socket
|> assign(:sitemap_version, new_version)
|> put_flash(:info, "Cache cleared. Sitemap regeneration started...")}
{:error, reason} ->
{:noreply, put_flash(socket, :error, "Failed to regenerate: #{inspect(reason)}")}
end
end
# Handle PubSub message when sitemap generation completes
@impl true
def handle_info({:sitemap_generated, %{url_count: count}}, socket) do
# Refresh config to get updated stats
config = Sitemap.get_config()
# Update sitemap version to force iframe reload
sitemap_version = get_sitemap_version(config)
{:noreply,
socket
|> assign(:generating, false)
|> assign(:config, config)
|> assign(:sitemap_version, sitemap_version)
|> put_flash(:info, "Sitemap generated successfully (#{count} URLs)")}
end
# Handle PubSub message when settings change (style, sources, etc.)
@impl true
def handle_info({:sitemap_settings_changed, %{type: :style_changed, version: version}}, socket) do
# Refresh config to get updated style
config = Sitemap.get_config()
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, version)}
end
@impl true
def handle_info({:sitemap_settings_changed, %{type: type}}, socket) do
# Handle other settings changes (sources, schedule, etc.)
config = Sitemap.get_config()
new_version = DateTime.utc_now() |> DateTime.to_unix()
{:noreply,
socket
|> assign(:config, config)
|> assign(:sitemap_version, new_version)
|> maybe_flash_for_setting(type)}
end
defp maybe_flash_for_setting(socket, :source_changed), do: socket
defp maybe_flash_for_setting(socket, :schedule_changed), do: socket
defp maybe_flash_for_setting(socket, :interval_changed), do: socket
defp maybe_flash_for_setting(socket, :html_changed), do: socket
defp maybe_flash_for_setting(socket, :sitemap_toggled), do: socket
defp maybe_flash_for_setting(socket, _), do: socket
# Broadcast settings change to all connected admin sessions
defp broadcast_settings_change(type, data) do
PubSubManager.broadcast(
"sitemap:settings",
{:sitemap_settings_changed, Map.put(data, :type, type)}
)
rescue
# PubSub may not be available in all environments
_ -> :ok
end
# Maps old HTML style names to new XSL style names
# hierarchical -> cards, grouped -> table, flat -> minimal
def get_xsl_style(html_style) do
case html_style do
"hierarchical" -> "cards"
"grouped" -> "table"
"flat" -> "minimal"
style when style in ["table", "cards", "minimal"] -> style
_ -> "table"
end
end
# Format ISO8601 timestamp string to human-readable format with system timezone
def format_timestamp(nil), do: "Never"
def format_timestamp(iso_string) when is_binary(iso_string) do
case DateTime.from_iso8601(iso_string) do
{:ok, dt, _} ->
# Use fake user with nil timezone to get system timezone from Settings
fake_user = %{user_timezone: nil}
date_str = UtilsDate.format_date_with_user_timezone(dt, fake_user)
time_str = UtilsDate.format_time_with_user_timezone(dt, fake_user)
"#{date_str} #{time_str}"
_ ->
iso_string
end
end
def format_timestamp(_), do: "Never"
# Generate sitemap version string for cache busting
# Uses last_generated timestamp or current time
defp get_sitemap_version(config) do
case config.last_generated do
nil ->
# No sitemap generated yet, use current timestamp
DateTime.utc_now() |> DateTime.to_unix()
iso_string when is_binary(iso_string) ->
case DateTime.from_iso8601(iso_string) do
{:ok, dt, _} -> DateTime.to_unix(dt)
_ -> DateTime.utc_now() |> DateTime.to_unix()
end
end
end
end