fix: templates are not copied automatically to projects

This commit is contained in:
2026-04-25 07:25:56 +02:00
parent 2296ff0e99
commit 6d86d0ce3f
11 changed files with 160 additions and 107 deletions

View File

@@ -6,7 +6,6 @@ defmodule BDS.Projects do
alias BDS.Persistence
alias BDS.Projects.Project
alias BDS.Repo
alias BDS.StarterTemplates
alias BDS.Slug
alias BDS.Templates
@@ -59,7 +58,6 @@ defmodule BDS.Projects do
})
|> Repo.insert!()
:ok = StarterTemplates.install(project)
{:ok, _templates} = Templates.rebuild_templates_from_files(project.id)
project
end)
@@ -94,7 +92,6 @@ defmodule BDS.Projects do
})
|> Repo.insert!()
:ok = StarterTemplates.install(project)
{:ok, _templates} = Templates.rebuild_templates_from_files(project.id)
project
end)

View File

@@ -14,6 +14,7 @@ defmodule BDS.Rendering do
alias BDS.Rendering.FileSystem
alias BDS.Rendering.Filters
alias BDS.Repo
alias BDS.StarterTemplates
alias BDS.Posts.Post
alias BDS.Posts.Translation
alias BDS.Tags.Tag
@@ -46,9 +47,14 @@ defmodule BDS.Rendering do
end
defp load_template_source(project_id, kind, slug) do
project = Projects.get_project!(project_id)
case select_template(project_id, kind, slug) do
nil -> {:error, :template_not_found}
%Template{} = template -> published_template_body(template)
%Template{} = template ->
published_template_body(template)
nil ->
load_bundled_template_source(project, kind, slug)
end
end
@@ -60,7 +66,7 @@ defmodule BDS.Rendering do
template.status == :published and
template.enabled == true and template.slug == ^slug,
limit: 1
) || select_template(project_id, kind, nil)
)
end
defp select_template(project_id, kind, nil) do
@@ -97,13 +103,12 @@ defmodule BDS.Rendering do
defp render_template(project_id, source, assigns) do
with {:ok, template_ast} <- Liquex.parse(source) do
project = Projects.get_project!(project_id)
template_root = Path.join(Projects.project_data_dir(project), "templates")
context =
Liquex.Context.new(assigns,
static_environment: assigns,
filter_module: Filters,
file_system: FileSystem.new(template_root)
file_system: FileSystem.new(StarterTemplates.template_roots(project))
)
{result, _context} = Liquex.render!(template_ast, context)
@@ -113,6 +118,30 @@ defmodule BDS.Rendering do
error -> {:error, error}
end
defp load_bundled_template_source(project, kind, slug) do
desired_slug = bundled_template_slug(kind, slug)
if is_binary(desired_slug) do
file_system = project |> StarterTemplates.template_roots() |> FileSystem.new()
source = Liquex.FileSystem.read_template_file(file_system, desired_slug)
case Frontmatter.parse_document(source) do
{:ok, %{body: body}} -> {:ok, body}
{:error, :invalid_frontmatter} -> {:ok, source}
{:error, reason} -> {:error, reason}
end
else
{:error, :template_not_found}
end
rescue
error in [Liquex.Error] ->
_ = error
{:error, :template_not_found}
end
defp bundled_template_slug(_kind, slug) when is_binary(slug) and slug != "", do: slug
defp bundled_template_slug(kind, _slug), do: StarterTemplates.default_slug(kind)
defp post_assigns(project_id, assigns) do
metadata = project_metadata(project_id)

View File

@@ -1,13 +1,17 @@
defmodule BDS.Rendering.FileSystem do
@moduledoc false
defstruct [:root_path]
defstruct [:root_paths]
def new(root_path) do
%__MODULE__{root_path: root_path}
def new(root_paths) when is_list(root_paths) do
%__MODULE__{root_paths: Enum.uniq(root_paths)}
end
def full_path(%__MODULE__{root_path: root_path}, template_path) do
def new(root_path) when is_binary(root_path) do
new([root_path])
end
def full_path(%__MODULE__{root_paths: root_paths}, template_path) do
normalized_path = to_string(template_path)
cond do
@@ -21,7 +25,16 @@ defmodule BDS.Rendering.FileSystem do
raise Liquex.Error, message: "Illegal template path '#{template_path}'"
true ->
Path.expand(Path.join(root_path, normalized_path <> ".liquid"))
root_paths
|> Enum.map(&Path.expand(Path.join(&1, normalized_path <> ".liquid")))
|> Enum.find(&File.regular?/1)
|> case do
nil ->
Path.expand(Path.join(List.first(root_paths) || ".", normalized_path <> ".liquid"))
path ->
path
end
end
end
end

View File

@@ -1,62 +1,22 @@
defmodule BDS.StarterTemplates do
@moduledoc false
alias BDS.Frontmatter
alias BDS.Projects
@top_level_templates [
%{file_name: "single-post.liquid", slug: "single-post", title: "Single Post", kind: :post},
%{file_name: "post-list.liquid", slug: "post-list", title: "Post List", kind: :list},
%{file_name: "not-found.liquid", slug: "not-found", title: "Not Found", kind: :not_found}
]
def install(project) do
source_root = Path.join(Application.app_dir(:bds, "priv/starter_templates"), "templates")
target_root = Path.join(Projects.project_data_dir(project), "templates")
:ok = File.mkdir_p(target_root)
source_root
|> list_files()
|> Enum.each(fn source_path ->
relative_path = Path.relative_to(source_path, source_root)
target_path = Path.join(target_root, relative_path)
:ok = File.mkdir_p(Path.dirname(target_path))
unless File.exists?(target_path) do
case Enum.find(@top_level_templates, &(&1.file_name == relative_path)) do
nil ->
File.cp!(source_path, target_path)
template ->
body = File.read!(source_path)
File.write!(
target_path,
Frontmatter.serialize_document(
[
{:id, Ecto.UUID.generate()},
{:slug, template.slug},
{:title, template.title},
{:kind, template.kind},
{:enabled, true},
{:version, 1}
],
body
)
)
end
end
end)
:ok
def root_path do
Path.join(Application.app_dir(:bds, "priv/starter_templates"), "templates")
end
defp list_files(root) do
root
|> Path.join("**/*")
|> Path.wildcard(match_dot: true)
|> Enum.reject(&File.dir?/1)
|> Enum.sort()
def template_roots(project) do
[Path.join(Projects.project_data_dir(project), "templates"), root_path()]
end
def default_slug(:post), do: "single-post"
def default_slug(:list), do: "post-list"
def default_slug(:not_found), do: "not-found"
def default_slug(_kind), do: nil
def default_template?(kind, slug) when is_binary(slug) do
default_slug(kind) == slug
end
end