Packages

phoenix_kit

1.7.19
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
phoenix_kit lib modules blogging web new.ex
Raw

lib/modules/blogging/web/new.ex

defmodule PhoenixKit.Modules.Blogging.Web.New do
@moduledoc """
LiveView for creating a new blog workspace.
"""
use PhoenixKitWeb, :live_view
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Blogging
alias PhoenixKit.Modules.Blogging.PubSub, as: BloggingPubSub
alias PhoenixKit.Modules.Blogging.Storage
alias PhoenixKit.Settings
alias PhoenixKit.Utils.Routes
def mount(_params, _session, socket) do
{initial_params, auto?, last_generated_slug} = normalize_slug_params(%{}, true, "")
socket =
socket
|> assign(:project_title, Settings.get_setting("project_title", "PhoenixKit"))
|> assign(:page_title, gettext("Create Blog"))
|> assign(
:current_path,
Routes.path("/admin/settings/blogging/new", locale: socket.assigns.current_locale_base)
)
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, initial_params)
|> assign(:form, new_blog_form(initial_params))
|> assign(:enabled_languages, Storage.enabled_language_codes())
|> assign(:endpoint_url, nil)
{:ok, socket}
end
def handle_params(_params, uri, socket) do
endpoint_url = extract_endpoint_url(uri)
{:noreply, assign(socket, :endpoint_url, endpoint_url)}
end
def handle_event("update_new_blog", %{"blog" => params}, socket) do
{normalized_params, auto?, last_generated_slug} =
normalize_slug_params(
params,
socket.assigns.slug_autogenerated?,
socket.assigns.last_generated_slug
)
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))}
end
def handle_event("manual_slug", %{"blog" => %{"slug" => value}}, socket) do
current_params = socket.assigns.form_params
name = Map.get(current_params, "name", "")
auto_slug = if name == "", do: "", else: Blogging.slugify(name)
trimmed_value = value |> to_string() |> String.trim()
cond do
# Empty slug - revert to auto-generated
trimmed_value == "" ->
updated = Map.put(current_params, "slug", auto_slug)
{:noreply,
socket
|> assign(:slug_autogenerated?, true)
|> assign(:last_generated_slug, auto_slug)
|> assign(:form_params, updated)
|> assign(:form, new_blog_form(updated))}
# Manually entered slug matches auto-generated - keep autogenerated flag
trimmed_value == auto_slug ->
updated = Map.put(current_params, "slug", auto_slug)
{:noreply,
socket
|> assign(:slug_autogenerated?, true)
|> assign(:last_generated_slug, auto_slug)
|> assign(:form_params, updated)
|> assign(:form, new_blog_form(updated))}
# Any other manual slug - just store it as-is (validation happens on submit)
true ->
updated = Map.put(current_params, "slug", trimmed_value)
{:noreply,
socket
|> assign(:slug_autogenerated?, false)
|> assign(:last_generated_slug, trimmed_value)
|> assign(:form_params, updated)
|> assign(:form, new_blog_form(updated))
|> clear_flash()}
end
end
def handle_event("add_blog", %{"blog" => params}, socket) do
# Use raw params for validation - don't normalize/sanitize
name = params |> Map.get("name", "") |> String.trim()
mode = params |> Map.get("mode", "timestamp")
slug = params |> Map.get("slug", "") |> String.trim()
# Still normalize for UI updates in case of error
{normalized_params, auto?, last_generated_slug} =
normalize_slug_params(
params,
socket.assigns.slug_autogenerated?,
socket.assigns.last_generated_slug
)
# Pass empty string if slug is empty to trigger auto-generation
slug_to_validate = if slug == "", do: nil, else: slug
case Blogging.add_blog(name, mode, slug_to_validate) do
{:ok, blog} ->
# Broadcast blog created for live dashboard updates
BloggingPubSub.broadcast_blog_created(blog)
{:noreply,
socket
|> put_flash(:info, gettext("Blog \"%{name}\" created", name: blog["name"]))
|> push_navigate(
to: Routes.path("/admin/settings/blogging", locale: socket.assigns.current_locale_base)
)}
{:error, :already_exists} ->
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))
|> put_flash(:error, gettext("That blog already exists"))}
{:error, :invalid_name} ->
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))
|> put_flash(:error, gettext("Please enter a valid blog name"))}
{:error, :invalid_mode} ->
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))
|> put_flash(:error, gettext("Invalid storage mode"))}
{:error, :invalid_slug} ->
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))
|> put_flash(
:error,
gettext(
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-blog-name)"
)
)}
{:error, _reason} ->
{:noreply,
socket
|> assign(:slug_autogenerated?, auto?)
|> assign(:last_generated_slug, last_generated_slug)
|> assign(:form_params, normalized_params)
|> assign(:form, new_blog_form(normalized_params))
|> put_flash(:error, gettext("Failed to add blog"))}
end
end
def handle_event("cancel", _params, socket) do
{:noreply,
push_navigate(socket,
to: Routes.path("/admin/settings/blogging", locale: socket.assigns.current_locale_base)
)}
end
defp new_blog_form(params) when is_map(params) do
defaults = %{"name" => "", "slug" => "", "mode" => "timestamp"}
values = Map.merge(defaults, params)
to_form(values, as: :blog)
end
defp normalize_slug_params(params, slug_autogenerated?, last_generated_slug) do
mode =
params
|> Map.get("mode", "timestamp")
|> to_string()
|> String.downcase()
|> case do
"slug" -> "slug"
_ -> "timestamp"
end
name =
params
|> Map.get("name", "")
|> to_string()
|> String.trim()
slug_input =
params
|> Map.get("slug", "")
|> to_string()
trimmed_slug = String.trim(slug_input)
# Keep user input as-is for validation - don't sanitize here
trimmed_input = trimmed_slug
auto_slug =
if name == "" do
""
else
Blogging.slugify(name)
end
if slug_autogenerated? do
slug_value = auto_slug
{%{"name" => name, "slug" => slug_value, "mode" => mode}, true, slug_value}
else
slug_value =
case trimmed_input do
"" -> auto_slug
value -> value
end
auto? = slug_value == auto_slug
new_last_generated = if auto?, do: slug_value, else: last_generated_slug
{%{"name" => name, "slug" => slug_value, "mode" => mode}, auto?, new_last_generated}
end
end
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: ""
end