Packages

phoenix_kit

1.7.81
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 publishing web controller slug_resolution.ex
Raw

lib/modules/publishing/web/controller/slug_resolution.ex

defmodule PhoenixKit.Modules.Publishing.Web.Controller.SlugResolution do
@moduledoc """
URL slug resolution for the publishing controller.
Handles resolving URL slugs to internal slugs, including:
- Per-language custom URL slugs
- Previous URL slugs for 301 redirects
- DB-based slug lookups
"""
alias PhoenixKit.Modules.Languages.DialectMapper
alias PhoenixKit.Modules.Publishing
alias PhoenixKit.Modules.Publishing.Web.Controller.Language
alias PhoenixKit.Modules.Publishing.Web.HTML, as: PublishingHTML
# ============================================================================
# URL Slug Resolution
# ============================================================================
@doc """
Resolves URL slug to internal slug using cache/DB.
Returns:
- `{:redirect, url}` for 301 redirect to new URL
- `{:ok, identifier}` for resolved internal slug
- `:passthrough` for direct use
"""
def resolve_url_slug(group_slug, {:slug, url_slug}, language) do
# Resolve base language codes (de, en) to stored dialect codes (de-DE, en-US)
# before DB queries, since content rows store full BCP-47 dialect codes
db_language = resolve_language_for_db(language)
case Publishing.find_by_url_slug(group_slug, db_language, url_slug) do
{:ok, cached_post} ->
internal_slug = cached_post.slug
if internal_slug == url_slug do
# URL slug matches internal slug - no resolution needed
:passthrough
else
# URL slug differs from internal slug - use resolved identifier
{:ok, {:slug, internal_slug}}
end
{:error, :not_found} ->
# Not found in current slugs - check previous slugs for 301 redirect
case Publishing.find_by_previous_url_slug(group_slug, db_language, url_slug) do
{:ok, cached_post} ->
# Found in previous slugs - redirect to current URL
current_url_slug =
Map.get(cached_post[:language_slugs] || %{}, db_language, cached_post.slug)
redirect_url =
build_post_redirect_url(group_slug, cached_post, language, current_url_slug)
{:redirect, redirect_url}
{:error, _} ->
:passthrough
end
end
end
# Non-slug modes pass through directly
def resolve_url_slug(_group_slug, _identifier, _language), do: :passthrough
@doc """
Resolves a URL slug to the internal post slug.
Used by versioned URL handler and other places that need the internal slug.
"""
def resolve_url_slug_to_internal(group_slug, url_slug, language) do
db_language = resolve_language_for_db(language)
case Publishing.find_by_url_slug(group_slug, db_language, url_slug) do
{:ok, cached_post} ->
cached_post.slug || cached_post[:slug]
{:error, _} ->
# Not found in cache/DB - use as-is
url_slug
end
end
# ============================================================================
# Redirect URL Building
# ============================================================================
@doc """
Builds redirect URL for 301 redirects from cached post data.
"""
def build_post_redirect_url(group_slug, cached_post, language, url_slug) do
# Build post struct with minimal fields needed for URL generation
post = %{
slug: cached_post.slug,
url_slug: url_slug,
mode: cached_post.mode,
date: cached_post.date,
time: cached_post.time,
language_slugs: cached_post.language_slugs
}
PublishingHTML.build_post_url(group_slug, post, language)
end
# ============================================================================
# Language Resolution
# ============================================================================
# Resolves a URL language code to the stored dialect code for DB queries.
# URL paths use base codes (de, en, fr) but content rows store full
# BCP-47 dialect codes (de-DE, en-US, fr-FR).
defp resolve_language_for_db(language) do
if Language.base_code?(language) do
Language.find_dialect_for_base(language, Language.get_enabled_languages()) ||
DialectMapper.base_to_dialect(language)
else
language
end
end
end