Search documentation of phoenix_kit

# PhoenixKitWeb.Live.Modules.Blogging.Storage (phoenix_kit v1.6.19)

View Source

Filesystem storage helpers for blogging posts.

Content is stored under:

    priv/blogging/<blog>/<YYYY-MM-DD>/<HH:MM>/<language>.phk

Where <language> is determined by the site's content language setting. Files use the .phk (PhoenixKit) format, which supports XML-style component markup for building pages with swappable design variants.

# Summary

## Types

post()

## Functions

absolute_path(relative_path)

Returns the absolute path for a relative blogging path.

add_language_to_post(blog_slug, post_path, language_code)

Adds a new language file to an existing post by copying metadata from an existing language.

add_language_to_post_slug_mode(blog_slug, post_slug, language_code)

Adds a new language file to a slug-mode post.

count_posts_on_date(blog_slug, date)

Counts the number of posts on a specific date for a blog. Used to determine if time should be included in URLs.

create_post(blog_slug, audit_meta \\ %{})

Creates a new post, returning its metadata and content. Creates only the primary language file. Additional languages can be added later.

create_post_slug_mode(blog_slug, title \\ nil, preferred_slug \\ nil, audit_meta \\ %{})

Creates a slug-mode post, returning metadata and paths for the primary language.

enabled_language_codes()

Returns all enabled language codes for multi-language support. Falls back to content language if Languages module is disabled.

ensure_blog_root(blog_slug)

Ensures the folder for a blog exists.

generate_unique_slug(blog_slug, title, preferred_slug \\ nil, opts \\ [])

Generates a unique slug based on title and optional preferred slug.

get_display_code(language_code, enabled_languages)

Determines the display code for a language based on whether multiple dialects of the same base language are enabled.

get_language_info(language_code)

Gets language details (name, flag) for a given language code.

language_filename()

Returns the filename for language-specific posts based on the site's primary content language setting.

language_filename(language_code)

Returns the filename for a specific language code.

list_posts(blog_slug, preferred_language \\ nil)

Lists posts for the given blog. Accepts optional preferred_language to show titles in user's language. Falls back to content language, then first available language.

list_posts_slug_mode(blog_slug, preferred_language \\ nil)

Lists slug-mode posts for the given blog.

list_times_on_date(blog_slug, date)

Lists all time folders (posts) for a specific date in a blog. Returns a list of time strings in HH:MM format, sorted.

move_blog_to_trash(blog_slug)

Moves a blog directory to trash by renaming it with a timestamp. The blog directory is moved to: trash/BLOGNAME-YYYY-MM-DD-HH-MM-SS

move_post_to_new_slug(blog_slug, post, new_slug, params, audit_meta \\ %{})

Moves slug-mode post files to a new slug directory.

order_languages_for_display(available_languages, enabled_languages)

Orders languages for display in the language switcher.

read_post(blog_slug, relative_path)

Reads a post for a specific language.

read_post_slug_mode(blog_slug, post_slug, language \\ nil)

Reads a slug-mode post, optionally for a specific language.

rename_blog_directory(old_slug, new_slug)

Renames a blog directory on disk when the slug changes.

root_path()

Returns the blogging root directory, creating it if needed. Always uses the parent application's priv directory.

slug_exists?(blog_slug, post_slug)

Checks if a slug already exists within the given blog.

update_post(blog_slug, post, params, audit_meta \\ %{})

Updates a post's metadata/content, moving files if needed. Preserves language and detects available languages.

update_post_slug_in_place(blog_slug, post, params, audit_meta \\ %{})

Updates slug-mode posts without moving them (slug unchanged).

update_post_slug_mode(blog_slug, post, params, audit_meta \\ %{})

Updates slug-mode posts in-place or moves them when the slug changes.

valid_slug?(slug)

Validates whether the given string is a slug and not a reserved language code.

validate_slug(slug)

Validates whether the given string is a valid slug format and not a reserved language code.

# Types

# post()

    @type post() :: %{
      blog: String.t() | nil,
      slug: String.t() | nil,
      date: Date.t() | nil,
      time: Time.t() | nil,
      path: String.t(),
      full_path: String.t(),
      metadata: map(),
      content: String.t(),
      language: String.t(),
      available_languages: [String.t()],
      mode: :slug | :timestamp | nil
    }

# Functions

# absolute_path(relative_path)

    @spec absolute_path(String.t()) :: String.t()

Returns the absolute path for a relative blogging path.

# add_language_to_post(blog_slug, post_path, language_code)

    @spec add_language_to_post(String.t(), String.t(), String.t()) ::
      {:ok, post()} | {:error, any()}

Adds a new language file to an existing post by copying metadata from an existing language.

# add_language_to_post_slug_mode(blog_slug, post_slug, language_code)

    @spec add_language_to_post_slug_mode(String.t(), String.t(), String.t()) ::
      {:ok, post()} | {:error, any()}

Adds a new language file to a slug-mode post.

# count_posts_on_date(blog_slug, date)

    @spec count_posts_on_date(String.t(), Date.t() | String.t()) :: non_neg_integer()

Counts the number of posts on a specific date for a blog. Used to determine if time should be included in URLs.

# create_post(blog_slug, audit_meta \\ %{})

    @spec create_post(String.t(), map() | keyword()) :: {:ok, post()} | {:error, any()}

Creates a new post, returning its metadata and content. Creates only the primary language file. Additional languages can be added later.

# create_post_slug_mode(blog_slug, title \\ nil, preferred_slug \\ nil, audit_meta \\ %{})

    @spec create_post_slug_mode(
      String.t(),
      String.t() | nil,
      String.t() | nil,
      map() | keyword()
    ) ::
      {:ok, post()} | {:error, any()}

Creates a slug-mode post, returning metadata and paths for the primary language.

# enabled_language_codes()

    @spec enabled_language_codes() :: [String.t()]

Returns all enabled language codes for multi-language support. Falls back to content language if Languages module is disabled.

# ensure_blog_root(blog_slug)

    @spec ensure_blog_root(String.t()) :: :ok | {:error, term()}

Ensures the folder for a blog exists.

# generate_unique_slug(blog_slug, title, preferred_slug \\ nil, opts \\ [])

    @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

# get_display_code(language_code, enabled_languages)

    @spec get_display_code(String.t(), [String.t()]) :: String.t()

Determines the display code for a language based on whether multiple dialects of the same base language are enabled.

If only one dialect of a base language is enabled (e.g., just "en-US"), returns the base code ("en") for cleaner display.

If multiple dialects are enabled (e.g., "en-US" and "en-GB"), returns the full dialect code ("en-US") to distinguish them.

## Parameters

- `language_code` - the full language/dialect code (e.g., "en-US")
- `enabled_languages` - list of all enabled language codes

## Examples

    # Only en-US enabled
    iex> get_display_code("en-US", ["en-US", "fr-FR"])
    "en"

    # Both en-US and en-GB enabled
    iex> get_display_code("en-US", ["en-US", "en-GB", "fr-FR"])
    "en-US"

# get_language_info(language_code)

    @spec get_language_info(String.t()) ::
      %{code: String.t(), name: String.t(), flag: String.t()} | nil

Gets language details (name, flag) for a given language code.

# language_filename()

    @spec language_filename() :: String.t()

Returns the filename for language-specific posts based on the site's primary content language setting.

# language_filename(language_code)

    @spec language_filename(String.t()) :: String.t()

Returns the filename for a specific language code.

# list_posts(blog_slug, preferred_language \\ nil)

    @spec list_posts(String.t(), String.t() | nil) :: [post()]

Lists posts for the given blog. Accepts optional preferred_language to show titles in user's language. Falls back to content language, then first available language.

# list_posts_slug_mode(blog_slug, preferred_language \\ nil)

    @spec list_posts_slug_mode(String.t(), String.t() | nil) :: [post()]

Lists slug-mode posts for the given blog.

# list_times_on_date(blog_slug, date)

    @spec list_times_on_date(String.t(), Date.t() | String.t()) :: [String.t()]

Lists all time folders (posts) for a specific date in a blog. Returns a list of time strings in HH:MM format, sorted.

# move_blog_to_trash(blog_slug)

    @spec move_blog_to_trash(String.t()) :: {:ok, String.t()} | {:error, term()}

Moves a blog directory to trash by renaming it with a timestamp. The blog directory is moved to: trash/BLOGNAME-YYYY-MM-DD-HH-MM-SS

Returns {:ok, new_name} on success or {:error, reason} on failure.

# move_post_to_new_slug(blog_slug, post, new_slug, params, audit_meta \\ %{})

    @spec move_post_to_new_slug(String.t(), post(), String.t(), map(), map() | keyword()) ::
      {:ok, post()} | {:error, any()}

Moves slug-mode post files to a new slug directory.

# order_languages_for_display(available_languages, enabled_languages)

    @spec order_languages_for_display([String.t()], [String.t()]) :: [String.t()]

Orders languages for display in the language switcher.

Order: primary language first, then languages with translations (sorted), then languages without translations (sorted). This ensures consistent order across all views regardless of which language is currently being edited.

## Parameters

- `available_languages` - list of language codes that have translations
- `enabled_languages` - list of all enabled language codes

## Returns

List of language codes in consistent display order.

# read_post(blog_slug, relative_path)

    @spec read_post(String.t(), String.t()) :: {:ok, post()} | {:error, any()}

Reads a post for a specific language.

# read_post_slug_mode(blog_slug, post_slug, language \\ nil)

    @spec read_post_slug_mode(String.t(), String.t(), String.t() | nil) ::
      {:ok, post()} | {:error, any()}

Reads a slug-mode post, optionally for a specific language.

# rename_blog_directory(old_slug, new_slug)

    @spec rename_blog_directory(String.t(), String.t()) :: :ok | {:error, term()}

Renames a blog directory on disk when the slug changes.

# root_path()

    @spec root_path() :: String.t()

Returns the blogging root directory, creating it if needed. Always uses the parent application's priv directory.

# slug_exists?(blog_slug, post_slug)

    @spec slug_exists?(String.t(), String.t()) :: boolean()

Checks if a slug already exists within the given blog.

# update_post(blog_slug, post, params, audit_meta \\ %{})

    @spec update_post(String.t(), post(), map(), map() | keyword()) ::
      {:ok, post()} | {:error, any()}

Updates a post's metadata/content, moving files if needed. Preserves language and detects available languages.

# update_post_slug_in_place(blog_slug, post, params, audit_meta \\ %{})

    @spec update_post_slug_in_place(String.t(), post(), map(), map() | keyword()) ::
      {:ok, post()} | {:error, any()}

Updates slug-mode posts without moving them (slug unchanged).

# update_post_slug_mode(blog_slug, post, params, audit_meta \\ %{})

    @spec update_post_slug_mode(String.t(), post(), map(), map() | keyword()) ::
      {:ok, post()} | {:error, any()}

Updates slug-mode posts in-place or moves them when the slug changes.

# valid_slug?(slug)

    @spec valid_slug?(String.t()) :: boolean()

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.

# validate_slug(slug)

    @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.