feat: preview working and template delete is in, too
This commit is contained in:
@@ -17,6 +17,7 @@ defmodule BDS.Desktop.ShellLive do
|
||||
alias BDS.Posts.Post
|
||||
alias BDS.Projects
|
||||
alias BDS.Repo
|
||||
alias BDS.Templates
|
||||
alias BDS.UI.{Commands, MenuBar, Registry, Session, Workbench}
|
||||
|
||||
@refresh_interval 1_500
|
||||
@@ -286,6 +287,34 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> reload_shell(workbench)}
|
||||
end
|
||||
|
||||
def handle_event("delete_sidebar_template", %{"id" => template_id}, socket) do
|
||||
case Repo.get(Templates.Template, template_id) do
|
||||
%Templates.Template{project_id: project_id} when project_id == socket.assigns.projects.active_project_id ->
|
||||
case Templates.delete_template(template_id) do
|
||||
{:ok, :deleted} ->
|
||||
workbench = Workbench.close_tab(socket.assigns.workbench, :templates, template_id)
|
||||
tab_meta = Map.delete(socket.assigns.tab_meta, {:templates, template_id})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:tab_meta, tab_meta)
|
||||
|> reload_shell(workbench)}
|
||||
|
||||
{:error, reason} ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> append_output_entry(translated("Delete") <> " " <> translated("Template"), inspect(reason), nil, "error")
|
||||
|> reload_shell(socket.assigns.workbench)}
|
||||
end
|
||||
|
||||
_other ->
|
||||
{:noreply,
|
||||
socket
|
||||
|> append_output_entry(translated("Delete") <> " " <> translated("Template"), inspect(:not_found), nil, "error")
|
||||
|> reload_shell(socket.assigns.workbench)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event("toggle_offline_mode", _params, socket) do
|
||||
socket = assign(socket, :offline_mode, not socket.assigns.offline_mode)
|
||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||
|
||||
@@ -350,27 +350,65 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
defp render_entity_sidebar(assigns) do
|
||||
~H"""
|
||||
<%= if Enum.any?(Map.get(@sidebar_data, :items, [])) do %>
|
||||
<div class="settings-nav-list">
|
||||
<div class={if(template_sidebar?(@sidebar_data), do: "chat-list-items", else: "settings-nav-list")}>
|
||||
<%= for item <- Map.get(@sidebar_data, :items, []) do %>
|
||||
<button
|
||||
class={["chat-list-item", if(sidebar_item_selected?(@workbench, item.route, item.id), do: "active")]}
|
||||
data-testid="sidebar-open-item"
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={translated(item.meta || "")}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={translated(item.meta || "")}
|
||||
>
|
||||
<span class="chat-item-content">
|
||||
<span class="chat-item-title"><%= item.title %></span>
|
||||
<span class="chat-item-date"><%= translated(item.meta || "") %></span>
|
||||
</span>
|
||||
</button>
|
||||
<%= if item.route == "templates" do %>
|
||||
<div
|
||||
class={["chat-list-item", if(sidebar_item_selected?(@workbench, item.route, item.id), do: "active")]}
|
||||
data-item-id={item.id}
|
||||
>
|
||||
<button
|
||||
class="chat-item-open"
|
||||
data-testid="sidebar-open-item"
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={translated(item.meta || "")}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={translated(item.meta || "")}
|
||||
>
|
||||
<span class="chat-item-content">
|
||||
<span class="chat-item-title"><%= item.title %></span>
|
||||
<span class="chat-item-date"><%= translated(item.meta || "") %></span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="chat-item-delete"
|
||||
data-testid="sidebar-delete-template"
|
||||
data-item-id={item.id}
|
||||
type="button"
|
||||
title={translated("Delete") <> " " <> translated("Template")}
|
||||
phx-click="delete_sidebar_template"
|
||||
phx-value-id={item.id}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<% else %>
|
||||
<button
|
||||
class={["chat-list-item", if(sidebar_item_selected?(@workbench, item.route, item.id), do: "active")]}
|
||||
data-testid="sidebar-open-item"
|
||||
data-route={item.route}
|
||||
data-item-id={item.id}
|
||||
data-open-title={item.title}
|
||||
data-open-subtitle={translated(item.meta || "")}
|
||||
type="button"
|
||||
phx-click="open_sidebar_item"
|
||||
phx-value-route={item.route}
|
||||
phx-value-id={item.id}
|
||||
phx-value-title={item.title}
|
||||
phx-value-subtitle={translated(item.meta || "")}
|
||||
>
|
||||
<span class="chat-item-content">
|
||||
<span class="chat-item-title"><%= item.title %></span>
|
||||
<span class="chat-item-date"><%= translated(item.meta || "") %></span>
|
||||
</span>
|
||||
</button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
@@ -426,6 +464,8 @@ defmodule BDS.Desktop.ShellLive.SidebarComponents do
|
||||
|
||||
defp translated(text, bindings \\ %{}), do: ShellData.translate(text, bindings, Process.get(:bds_ui_locale))
|
||||
|
||||
defp template_sidebar?(sidebar_data), do: Map.get(sidebar_data, :title) == "Templates"
|
||||
|
||||
defp group_year_month_counts(entries) do
|
||||
entries
|
||||
|> Enum.group_by(& &1.year)
|
||||
|
||||
@@ -52,7 +52,13 @@ defmodule BDS.Rendering do
|
||||
|
||||
case select_template(project_id, kind, slug) do
|
||||
%Template{} = template ->
|
||||
published_template_body(template)
|
||||
case published_template_body(template) do
|
||||
{:ok, _source} = ok ->
|
||||
ok
|
||||
|
||||
{:error, reason} = error ->
|
||||
maybe_load_bundled_template_source(project, kind, slug, template, reason, error)
|
||||
end
|
||||
|
||||
nil ->
|
||||
load_bundled_template_source(project, kind, slug)
|
||||
@@ -70,6 +76,23 @@ defmodule BDS.Rendering do
|
||||
)
|
||||
end
|
||||
|
||||
defp select_template(project_id, :post, nil) do
|
||||
case StarterTemplates.default_slug(:post) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
default_slug ->
|
||||
Repo.one(
|
||||
from template in Template,
|
||||
where:
|
||||
template.project_id == ^project_id and template.kind == :post and
|
||||
template.status == :published and
|
||||
template.enabled == true and template.slug == ^default_slug,
|
||||
limit: 1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp select_template(project_id, kind, nil) do
|
||||
Repo.one(
|
||||
from template in Template,
|
||||
@@ -139,11 +162,24 @@ defmodule BDS.Rendering do
|
||||
{:error, :template_not_found}
|
||||
end
|
||||
|
||||
defp maybe_load_bundled_template_source(project, kind, slug, template, reason, error)
|
||||
when reason in [:enoent, :template_not_found] do
|
||||
if template.content in [nil, ""] and StarterTemplates.default_template?(kind, template.slug) do
|
||||
load_bundled_template_source(project, kind, slug)
|
||||
else
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_load_bundled_template_source(_project, _kind, _slug, _template, _reason, error),
|
||||
do: error
|
||||
|
||||
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)
|
||||
template_context = template_render_context(project_id)
|
||||
|
||||
language =
|
||||
Map.get(assigns, :language, Map.get(assigns, "language", metadata.main_language || "en"))
|
||||
@@ -156,9 +192,16 @@ defmodule BDS.Rendering do
|
||||
post_tags = Map.get(post_record || %{}, :tags, []) || []
|
||||
canonical_post_paths = canonical_post_path_by_slug(project_id, main_language)
|
||||
canonical_media_paths = canonical_media_path_by_source_path(project_id)
|
||||
raw_content = Map.get(assigns, :content, Map.get(assigns, "content"))
|
||||
rendered_content = render_post_content(raw_content, canonical_post_paths, canonical_media_paths, language, template_context)
|
||||
incoming_links = link_contexts(project_id, post_id, :incoming, main_language)
|
||||
outgoing_links = link_contexts(project_id, post_id, :outgoing, main_language)
|
||||
|
||||
post_assigns =
|
||||
assigns
|
||||
|> Map.put(:content, rendered_content)
|
||||
|> Map.put(:raw_content, raw_content)
|
||||
|
||||
%{
|
||||
language: language,
|
||||
language_prefix:
|
||||
@@ -196,26 +239,35 @@ defmodule BDS.Rendering do
|
||||
backlinks: backlinks(incoming_links),
|
||||
canonical_post_path_by_slug: canonical_post_paths,
|
||||
canonical_media_path_by_source_path: canonical_media_paths,
|
||||
post_data_json_by_id: post_data_json(assigns, post_record),
|
||||
post: build_post_context(assigns, post_record, incoming_links, outgoing_links)
|
||||
post_data_json_by_id: post_data_json(post_assigns, post_record),
|
||||
post: build_post_context(post_assigns, post_record, incoming_links, outgoing_links)
|
||||
}
|
||||
end
|
||||
|
||||
defp list_assigns(project_id, assigns) do
|
||||
metadata = project_metadata(project_id)
|
||||
template_context = template_render_context(project_id)
|
||||
|
||||
language =
|
||||
Map.get(assigns, :language, Map.get(assigns, "language", metadata.main_language || "en"))
|
||||
|
||||
main_language = metadata.main_language || language
|
||||
posts = normalize_list_posts(Map.get(assigns, :posts, Map.get(assigns, "posts", [])))
|
||||
archive_context = Map.get(assigns, :archive_context, Map.get(assigns, "archive_context", %{}))
|
||||
|
||||
canonical_post_paths = canonical_post_path_by_slug(project_id, main_language)
|
||||
canonical_media_paths = canonical_media_path_by_source_path(project_id)
|
||||
posts =
|
||||
normalize_list_posts(
|
||||
Map.get(assigns, :posts, Map.get(assigns, "posts", [])),
|
||||
canonical_post_paths,
|
||||
canonical_media_paths,
|
||||
language,
|
||||
template_context
|
||||
)
|
||||
|
||||
pagination =
|
||||
normalize_pagination(Map.get(assigns, :pagination, Map.get(assigns, "pagination")), posts)
|
||||
|
||||
canonical_post_paths = canonical_post_path_by_slug(project_id, main_language)
|
||||
canonical_media_paths = canonical_media_path_by_source_path(project_id)
|
||||
day_blocks = build_day_blocks(posts)
|
||||
min_date = min_date(posts)
|
||||
max_date = max_date(posts)
|
||||
@@ -551,20 +603,29 @@ defmodule BDS.Rendering do
|
||||
post_path(post, prefix)
|
||||
end
|
||||
|
||||
defp normalize_list_posts(posts) do
|
||||
defp normalize_list_posts(posts, canonical_post_paths, canonical_media_paths, language, template_context) do
|
||||
Enum.map(posts, fn post ->
|
||||
post_record = load_post_record(post)
|
||||
raw_content =
|
||||
Map.get(
|
||||
post,
|
||||
:content,
|
||||
Map.get(post, "content", Map.get(post, :excerpt, Map.get(post, "excerpt", "")))
|
||||
)
|
||||
|
||||
%{
|
||||
id: Map.get(post, :id, Map.get(post, "id")),
|
||||
slug: Map.get(post, :slug, Map.get(post, "slug")),
|
||||
title: Map.get(post, :title, Map.get(post, "title")),
|
||||
content:
|
||||
Map.get(
|
||||
post,
|
||||
:content,
|
||||
Map.get(post, "content", Map.get(post, :excerpt, Map.get(post, "excerpt", "")))
|
||||
render_post_content(
|
||||
raw_content,
|
||||
canonical_post_paths,
|
||||
canonical_media_paths,
|
||||
language,
|
||||
template_context
|
||||
),
|
||||
raw_content: raw_content,
|
||||
excerpt:
|
||||
Map.get(post, :excerpt, Map.get(post, "excerpt", Map.get(post_record || %{}, :excerpt))),
|
||||
author: Map.get(post, :author, Map.get(post, "author", Map.get(post_record || %{}, :author))),
|
||||
@@ -602,6 +663,7 @@ defmodule BDS.Rendering do
|
||||
slug: Map.get(assigns, :slug, Map.get(assigns, "slug")),
|
||||
title: Map.get(assigns, :title, Map.get(assigns, "title")),
|
||||
content: Map.get(assigns, :content, Map.get(assigns, "content")),
|
||||
raw_content: Map.get(assigns, :raw_content, Map.get(assigns, "raw_content")),
|
||||
excerpt:
|
||||
Map.get(
|
||||
assigns,
|
||||
@@ -634,6 +696,20 @@ defmodule BDS.Rendering do
|
||||
}
|
||||
end
|
||||
|
||||
defp render_post_content(content, canonical_post_paths, canonical_media_paths, language, template_context) do
|
||||
Filters.render_markdown(content, canonical_post_paths, canonical_media_paths, language, template_context)
|
||||
end
|
||||
|
||||
defp template_render_context(project_id) do
|
||||
project = Projects.get_project!(project_id)
|
||||
|
||||
Liquex.Context.new(%{},
|
||||
static_environment: %{},
|
||||
filter_module: Filters,
|
||||
file_system: FileSystem.new(StarterTemplates.template_roots(project))
|
||||
)
|
||||
end
|
||||
|
||||
defp normalize_pagination(nil, posts) do
|
||||
total_items = length(posts)
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ defmodule BDS.Rendering.Filters do
|
||||
_language_prefix,
|
||||
context
|
||||
) do
|
||||
render_markdown(value, canonical_post_paths, canonical_media_paths, language, context)
|
||||
end
|
||||
|
||||
def render_markdown(value, canonical_post_paths, canonical_media_paths, language, context) do
|
||||
value
|
||||
|> to_string()
|
||||
|> replace_built_in_macros(language, context)
|
||||
|
||||
@@ -155,6 +155,8 @@ defmodule BDS.Templates do
|
||||
template
|
||||
end)
|
||||
|
||||
remove_stale_published_templates(project_id, project, template_paths)
|
||||
|
||||
{:ok, templates}
|
||||
end
|
||||
|
||||
@@ -380,6 +382,29 @@ defmodule BDS.Templates do
|
||||
|> Repo.insert_or_update!()
|
||||
end
|
||||
|
||||
defp remove_stale_published_templates(project_id, project, template_paths) do
|
||||
tracked_paths =
|
||||
template_paths
|
||||
|> Enum.map(&Path.relative_to(&1, Projects.project_data_dir(project)))
|
||||
|> MapSet.new()
|
||||
|
||||
Repo.all(
|
||||
from template in Template,
|
||||
where:
|
||||
template.project_id == ^project_id and
|
||||
template.status == :published and
|
||||
template.file_path != "" and
|
||||
not is_nil(template.file_path)
|
||||
)
|
||||
|> Enum.reject(&(MapSet.member?(tracked_paths, &1.file_path) or File.exists?(full_file_path(project_id, &1.file_path))))
|
||||
|> Enum.each(fn template ->
|
||||
clear_template_references(template)
|
||||
Repo.delete!(template)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp parse_template_kind(kind) when is_atom(kind), do: kind
|
||||
defp parse_template_kind("post"), do: :post
|
||||
defp parse_template_kind("list"), do: :list
|
||||
|
||||
@@ -53,7 +53,7 @@ version: 1
|
||||
{% endif %}
|
||||
<h2 class="post-title"><a href="{{ canonical_post_href }}">{{ post.title }}</a></h2>
|
||||
{% endif %}
|
||||
{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}
|
||||
{{ post.content }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@ version: 1
|
||||
{% endif %}
|
||||
<h2 class="post-title"><a href="{{ canonical_post_href }}">{{ post.title }}</a></h2>
|
||||
{% endif %}
|
||||
{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}
|
||||
{{ post.content }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
@@ -26,7 +26,7 @@ version: 1
|
||||
</div>
|
||||
{% endif %}
|
||||
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
|
||||
<div class="post">{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}</div>
|
||||
<div class="post">{{ post.content }}</div>
|
||||
</article>
|
||||
{% if backlinks.size > 0 %}
|
||||
<div class="single-post-backlinks" aria-label="{{ 'render.backlinks.ariaLabel' | i18n: language }}">
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
{% endif %}
|
||||
<h2 class="post-title"><a href="{{ canonical_post_href }}">{{ post.title }}</a></h2>
|
||||
{% endif %}
|
||||
{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}
|
||||
{{ post.content }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@
|
||||
{% endif %}
|
||||
<h2 class="post-title"><a href="{{ canonical_post_href }}">{{ post.title }}</a></h2>
|
||||
{% endif %}
|
||||
{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}
|
||||
{{ post.content }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<article class="single-post blog-post" data-template="single-post" data-pagefind-body>
|
||||
<div class="post">{{ post.content | markdown: post.id, post_data_json_by_id, canonical_post_path_by_slug, canonical_media_path_by_source_path, language, language_prefix }}</div>
|
||||
<div class="post">{{ post.content }}</div>
|
||||
</article>
|
||||
{% if backlinks.size > 0 %}
|
||||
<div class="single-post-backlinks" aria-label="{{ 'render.backlinks.ariaLabel' | i18n: language }}">
|
||||
|
||||
@@ -4246,9 +4246,11 @@ button svg * {
|
||||
|
||||
.chat-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--vscode-sideBar-border);
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
@@ -4271,6 +4273,26 @@ button svg * {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.chat-item-open {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.chat-item-open:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.chat-item-open:focus-visible {
|
||||
outline: 1px solid var(--vscode-focusBorder);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.chat-item-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -4282,6 +4304,27 @@ button svg * {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.chat-item-delete {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
padding: 0 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.chat-list-item:hover .chat-item-delete,
|
||||
.chat-list-item.active .chat-item-delete {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.chat-item-delete:hover {
|
||||
color: var(--vscode-errorForeground);
|
||||
}
|
||||
|
||||
.settings-nav-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -727,6 +727,44 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
refute html =~ ~s(phx-value-mode="visual")
|
||||
end
|
||||
|
||||
test "template sidebar exposes old-app style delete control and removes template rows", %{project: project} do
|
||||
assert {:ok, template} =
|
||||
BDS.Templates.create_template(%{
|
||||
project_id: project.id,
|
||||
title: "Sidebar Template",
|
||||
kind: :post,
|
||||
content: "<article>{{ post.content }}</article>"
|
||||
})
|
||||
|
||||
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='activity-button'][data-view='templates']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ "Sidebar Template"
|
||||
assert html =~ ~s(data-testid="sidebar-delete-template")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='sidebar-open-item'][data-item-id='#{template.id}']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(data-tab-type="templates")
|
||||
assert html =~ ~s(data-tab-id="#{template.id}")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='sidebar-delete-template'][data-item-id='#{template.id}']")
|
||||
|> render_click()
|
||||
|
||||
assert BDS.Repo.get(BDS.Templates.Template, template.id) == nil
|
||||
refute html =~ "Sidebar Template"
|
||||
refute html =~ ~s(data-tab-type="templates")
|
||||
refute html =~ ~s(data-tab-id="#{template.id}")
|
||||
end
|
||||
|
||||
defp seed_sidebar_posts(project_id) do
|
||||
now = Persistence.now_ms()
|
||||
|
||||
|
||||
@@ -259,7 +259,7 @@ defmodule BDS.GenerationTest do
|
||||
Posts.create_post(%{
|
||||
project_id: project.id,
|
||||
title: "Rendered Post",
|
||||
content: "Rendered body",
|
||||
content: "**Rendered** body",
|
||||
language: "en",
|
||||
template_slug: published_post_template.slug
|
||||
})
|
||||
@@ -280,7 +280,8 @@ defmodule BDS.GenerationTest do
|
||||
|
||||
post_html = File.read!(Path.join([temp_dir, "html", post_path]))
|
||||
assert post_html =~ "post-template"
|
||||
assert post_html =~ "Rendered body"
|
||||
assert post_html =~ ~s(<strong>Rendered</strong> body)
|
||||
refute post_html =~ "**Rendered** body"
|
||||
|
||||
assert "pagefind/index.json" in relative_paths
|
||||
assert "pagefind/pagefind-ui.js" in relative_paths
|
||||
|
||||
@@ -134,7 +134,7 @@ defmodule BDS.PreviewTest do
|
||||
Posts.create_post(%{
|
||||
project_id: project.id,
|
||||
title: "Draft Post",
|
||||
content: "Draft preview body",
|
||||
content: "**Draft** preview body",
|
||||
language: "en",
|
||||
template_slug: published_template.slug
|
||||
})
|
||||
@@ -146,7 +146,29 @@ defmodule BDS.PreviewTest do
|
||||
|
||||
assert draft_html =~ "preview-template"
|
||||
assert draft_html =~ "Draft Post"
|
||||
assert draft_html =~ "Draft preview body"
|
||||
assert draft_html =~ ~s(<strong>Draft</strong> preview body)
|
||||
refute draft_html =~ "**Draft** preview body"
|
||||
|
||||
assert {:ok, published_post} = Posts.publish_post(post.id)
|
||||
|
||||
published_datetime = DateTime.from_unix!(published_post.created_at, :millisecond)
|
||||
published_path =
|
||||
"/#{published_datetime.year}/#{String.pad_leading(Integer.to_string(published_datetime.month), 2, "0")}/#{String.pad_leading(Integer.to_string(published_datetime.day), 2, "0")}/#{published_post.slug}"
|
||||
|
||||
:inets.start()
|
||||
|
||||
assert {:ok, server} = BDS.Preview.start_preview(project.id)
|
||||
|
||||
assert {:ok, {{_version, 200, _reason}, _headers, published_html}} =
|
||||
:httpc.request(
|
||||
:get,
|
||||
{to_charlist("http://#{server.host}:#{server.port}#{published_path}?draft=true&post_id=#{published_post.id}"), []},
|
||||
[],
|
||||
body_format: :binary
|
||||
)
|
||||
|
||||
assert published_html =~ ~s(<strong>Draft</strong> preview body)
|
||||
refute published_html =~ "**Draft** preview body"
|
||||
|
||||
assert :ok = BDS.Preview.stop_preview(project.id)
|
||||
end
|
||||
|
||||
@@ -311,6 +311,59 @@ defmodule BDS.RenderingTest do
|
||||
assert rendered =~ "range=1711843200-1711929600"
|
||||
end
|
||||
|
||||
test "render_post_page falls back to bundled starter template when the published default template file is missing", %{
|
||||
project: project
|
||||
} do
|
||||
assert {:ok, _metadata} =
|
||||
BDS.Metadata.update_project_metadata(project.id, %{
|
||||
public_url: "https://example.com/blog",
|
||||
main_language: "en",
|
||||
blog_languages: ["en"]
|
||||
})
|
||||
|
||||
assert {:ok, template} =
|
||||
BDS.Templates.create_template(%{
|
||||
project_id: project.id,
|
||||
title: "Broken Default Post Template",
|
||||
kind: :post,
|
||||
content: "this custom template should not be used"
|
||||
})
|
||||
|
||||
assert {:ok, published_template} = BDS.Templates.publish_template(template.id)
|
||||
|
||||
BDS.Repo.update_all(
|
||||
from(template in BDS.Templates.Template,
|
||||
where: template.id == ^published_template.id
|
||||
),
|
||||
set: [slug: "single-post", file_path: "templates/single-post.liquid", content: nil]
|
||||
)
|
||||
|
||||
assert {:ok, post} =
|
||||
BDS.Posts.create_post(%{
|
||||
project_id: project.id,
|
||||
title: "Fallback Body",
|
||||
content: "**Rendered** body",
|
||||
language: "en"
|
||||
})
|
||||
|
||||
assert {:ok, published_post} = BDS.Posts.publish_post(post.id)
|
||||
|
||||
assert {:ok, rendered} =
|
||||
Rendering.render_post_page(project.id, nil, %{
|
||||
id: published_post.id,
|
||||
title: published_post.title,
|
||||
content: BDS.Posts.editor_body(published_post),
|
||||
slug: published_post.slug,
|
||||
language: published_post.language,
|
||||
excerpt: published_post.excerpt,
|
||||
template_slug: published_post.template_slug
|
||||
})
|
||||
|
||||
assert rendered =~ ~s(data-template="single-post")
|
||||
assert rendered =~ ~s(<strong>Rendered</strong> body)
|
||||
refute rendered =~ "this custom template should not be used"
|
||||
end
|
||||
|
||||
defp canonical_post_href(post) do
|
||||
datetime = DateTime.from_unix!(post.created_at, :millisecond)
|
||||
|
||||
|
||||
@@ -263,4 +263,32 @@ defmodule BDS.TemplatesTest do
|
||||
assert template.created_at == 101
|
||||
assert template.updated_at == 202
|
||||
end
|
||||
|
||||
test "rebuild_templates_from_files removes stale published default templates when no local template files exist", %{
|
||||
project: project
|
||||
} do
|
||||
now = BDS.Persistence.now_ms()
|
||||
|
||||
stale_template =
|
||||
%BDS.Templates.Template{}
|
||||
|> BDS.Templates.Template.changeset(%{
|
||||
id: Ecto.UUID.generate(),
|
||||
project_id: project.id,
|
||||
slug: "single-post",
|
||||
title: "Single Post",
|
||||
kind: :post,
|
||||
enabled: true,
|
||||
version: 1,
|
||||
file_path: "templates/single-post.liquid",
|
||||
status: :published,
|
||||
content: nil,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
})
|
||||
|> Repo.insert!()
|
||||
|
||||
assert {:ok, templates} = BDS.Templates.rebuild_templates_from_files(project.id)
|
||||
assert templates == []
|
||||
assert Repo.get(BDS.Templates.Template, stale_template.id) == nil
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user