Slug validation and generation for pages storage.
Handles slug format validation, uniqueness checking, URL slug validation for per-language slugs, and slug generation.
Summary
Functions
Clears custom url_slugs that conflict with a given directory slug.
Clears a specific url_slug from all translations of a single post.
Generates a unique slug based on title and optional preferred slug.
Checks if a slug already exists within the given pages group.
Validates whether the given string is a slug and not a reserved language code.
Validates whether the given string is a valid slug format and not a reserved language code.
Validates a per-language URL slug for uniqueness within a group+language combination.
Functions
Clears custom url_slugs that conflict with a given directory slug.
When a new post is created with directory slug X, any other posts that have custom url_slug = X need to have their url_slugs cleared (so they fall back to their own directory slug). Directory slugs have priority.
Returns the list of {post_slug, language} tuples that were cleared.
Clears a specific url_slug from all translations of a single post.
This is used when saving a post with a conflicting url_slug - we clear the url_slug from all translations of the same post that have that value.
Returns the list of language codes that were cleared.
@spec generate_unique_slug(String.t(), String.t(), String.t() | nil, keyword()) :: {:ok, String.t()} | {:error, :invalid_format | :reserved_language_code}
Generates a unique slug based on title and optional preferred slug.
Returns {:ok, slug} or {:error, reason} where reason can be:
:invalid_format- slug has invalid format:reserved_language_code- slug is a reserved language code
Checks if a slug already exists within the given pages group.
Validates whether the given string is a slug and not a reserved language code.
Blog slugs cannot be language codes (like 'en', 'es', 'fr') to prevent routing ambiguity.
@spec validate_slug(String.t()) :: {:ok, String.t()} | {:error, :invalid_format | :reserved_language_code}
Validates whether the given string is a valid slug format and not a reserved language code.
Returns:
{:ok, slug}if valid{:error, :invalid_format}if format is invalid{:error, :reserved_language_code}if slug is a language code
Blog slugs cannot be language codes (like 'en', 'es', 'fr') to prevent routing ambiguity.
@spec validate_url_slug(String.t(), String.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | {:error, atom()}
Validates a per-language URL slug for uniqueness within a group+language combination.
URL slugs have the same format requirements as directory slugs, plus:
- Cannot be reserved route words (admin, api, assets, etc.)
- Must be unique within the group+language combination
Parameters
group_slug- The pages groupurl_slug- The URL slug to validatelanguage- The language codeexclude_post_slug- Optional post slug to exclude from uniqueness check (for updates)
Returns
{:ok, url_slug}- Valid and unique{:error, :invalid_format}- Invalid format{:error, :reserved_language_code}- Is a language code{:error, :reserved_route_word}- Is a reserved route word{:error, :slug_already_exists}- Already in use for this language{:error, :conflicts_with_directory_slug}- Conflicts with another post's directory slug