fix: incremental rendering more on par with bDS
This commit is contained in:
@@ -263,27 +263,39 @@ defmodule BDS.Desktop.ShellCommands do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp dispatch("apply_site_validation", project, params) do
|
defp dispatch("apply_site_validation", project, params) do
|
||||||
report = normalize_apply_validation_report(BDS.MapUtils.attr(params, :report, %{}))
|
validation_report = normalize_apply_validation_report(BDS.MapUtils.attr(params, :report, %{}))
|
||||||
|
group_id = task_group_id("apply_site_validation")
|
||||||
|
attrs = %{group_id: group_id, group_name: "Apply Site Validation"}
|
||||||
|
|
||||||
queue_task(
|
{:ok, prepare_task} =
|
||||||
project,
|
Tasks.submit_task(
|
||||||
"apply_site_validation",
|
"Prepare Validation Apply",
|
||||||
"Apply Site Validation",
|
fn report ->
|
||||||
"Generation",
|
{:ok, preparation} =
|
||||||
fn report_fn ->
|
Generation.prepare_validation_apply(project.id, validation_report,
|
||||||
{:ok, _apply} =
|
on_progress: report
|
||||||
Generation.apply_validation(project.id, report,
|
|
||||||
on_progress: scaled_progress_reporter(report_fn, 0.0, 0.85)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, validation} =
|
Map.put(preparation, :project_id, project.id)
|
||||||
Generation.validate_site(project.id, @site_sections,
|
end,
|
||||||
on_progress: scaled_progress_reporter(report_fn, 0.85, 1.0)
|
attrs
|
||||||
)
|
)
|
||||||
|
|
||||||
site_validation_result(project.id, validation)
|
Task.start(fn ->
|
||||||
end
|
run_apply_validation_sequence(project, validation_report, group_id, attrs, prepare_task.id)
|
||||||
)
|
end)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
kind: "task_queued",
|
||||||
|
action: "apply_site_validation",
|
||||||
|
title: "Apply Site Validation",
|
||||||
|
message: "Apply Site Validation tasks queued",
|
||||||
|
project_id: project.id,
|
||||||
|
task_id: prepare_task.id,
|
||||||
|
task_group_id: group_id,
|
||||||
|
panel_tab: "tasks"
|
||||||
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp dispatch("metadata_diff", project, _params) do
|
defp dispatch("metadata_diff", project, _params) do
|
||||||
@@ -463,6 +475,124 @@ defmodule BDS.Desktop.ShellCommands do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp run_apply_validation_sequence(project, validation_report, group_id, attrs, prepare_task_id) do
|
||||||
|
with :ok <-
|
||||||
|
wait_for_group_phase(group_id, ["Prepare Validation Apply"], @rebuild_phase_timeout),
|
||||||
|
{:ok, preparation} <- completed_task_result(prepare_task_id),
|
||||||
|
:ok <-
|
||||||
|
run_apply_validation_render_tasks(
|
||||||
|
project,
|
||||||
|
validation_report,
|
||||||
|
group_id,
|
||||||
|
attrs,
|
||||||
|
preparation
|
||||||
|
) do
|
||||||
|
if validation_apply_changed?(preparation) do
|
||||||
|
:ok = run_apply_validation_refresh_task(project, group_id, attrs, :calendar)
|
||||||
|
end
|
||||||
|
|
||||||
|
submit_apply_validation_check_task(project, attrs)
|
||||||
|
else
|
||||||
|
_other -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run_apply_validation_render_tasks(project, validation_report, group_id, attrs, preparation) do
|
||||||
|
sections = Map.get(preparation, :sections_to_render, [])
|
||||||
|
task_names = Enum.map(sections, &apply_validation_section_task_name/1)
|
||||||
|
|
||||||
|
Enum.each(sections, fn section ->
|
||||||
|
task_name = apply_validation_section_task_name(section)
|
||||||
|
|
||||||
|
{:ok, _task} =
|
||||||
|
Tasks.submit_task(
|
||||||
|
task_name,
|
||||||
|
fn report ->
|
||||||
|
{:ok, _result} =
|
||||||
|
Generation.apply_validation_section(project.id, validation_report, section,
|
||||||
|
on_progress: report
|
||||||
|
)
|
||||||
|
|
||||||
|
report.(1.0, apply_validation_section_complete_message(section))
|
||||||
|
:ok
|
||||||
|
end,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
case task_names do
|
||||||
|
[] -> :ok
|
||||||
|
names -> wait_for_group_phase(group_id, names, @rebuild_phase_timeout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp run_apply_validation_refresh_task(project, group_id, attrs, kind) do
|
||||||
|
task_name = apply_validation_refresh_task_name(kind)
|
||||||
|
|
||||||
|
{:ok, _task} =
|
||||||
|
Tasks.submit_task(
|
||||||
|
task_name,
|
||||||
|
fn report ->
|
||||||
|
{:ok, _result} =
|
||||||
|
Generation.refresh_validation_ancillary_outputs(project.id, kind, on_progress: report)
|
||||||
|
|
||||||
|
report.(1.0, apply_validation_refresh_complete_message(kind))
|
||||||
|
:ok
|
||||||
|
end,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
|
||||||
|
wait_for_group_phase(group_id, [task_name], @rebuild_phase_timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp submit_apply_validation_check_task(project, attrs) do
|
||||||
|
{:ok, _task} =
|
||||||
|
Tasks.submit_task(
|
||||||
|
"Validate Site",
|
||||||
|
fn report ->
|
||||||
|
{:ok, validation} =
|
||||||
|
Generation.validate_site(project.id, @site_sections, on_progress: report)
|
||||||
|
|
||||||
|
site_validation_result(project.id, validation)
|
||||||
|
end,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
defp completed_task_result(task_id) do
|
||||||
|
case Tasks.get_task(task_id) do
|
||||||
|
%{status: :completed, result: result} when is_map(result) -> {:ok, result}
|
||||||
|
%{status: :failed, error: error} -> {:error, error}
|
||||||
|
_other -> {:error, :task_result_unavailable}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_apply_changed?(preparation) do
|
||||||
|
Map.get(preparation, :sections_to_render, []) != [] or
|
||||||
|
Map.get(preparation, :deleted_url_count, 0) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
defp apply_validation_section_task_name(:core), do: "Render Site Core"
|
||||||
|
defp apply_validation_section_task_name(:single), do: "Render Single Posts"
|
||||||
|
defp apply_validation_section_task_name(:category), do: "Render Category Archives"
|
||||||
|
defp apply_validation_section_task_name(:tag), do: "Render Tag Archives"
|
||||||
|
defp apply_validation_section_task_name(:date), do: "Render Date Archives"
|
||||||
|
|
||||||
|
defp apply_validation_section_complete_message(:core), do: "Render Site Core complete"
|
||||||
|
defp apply_validation_section_complete_message(:single), do: "Render Single Posts complete"
|
||||||
|
|
||||||
|
defp apply_validation_section_complete_message(:category),
|
||||||
|
do: "Render Category Archives complete"
|
||||||
|
|
||||||
|
defp apply_validation_section_complete_message(:tag), do: "Render Tag Archives complete"
|
||||||
|
defp apply_validation_section_complete_message(:date), do: "Render Date Archives complete"
|
||||||
|
|
||||||
|
defp apply_validation_refresh_task_name(:calendar), do: "Regenerate Calendar"
|
||||||
|
|
||||||
|
defp apply_validation_refresh_complete_message(:calendar), do: "Regenerate Calendar complete"
|
||||||
|
|
||||||
defp translation_fill_enabled?(metadata) do
|
defp translation_fill_enabled?(metadata) do
|
||||||
([metadata.main_language] ++ metadata.blog_languages)
|
([metadata.main_language] ++ metadata.blog_languages)
|
||||||
|> Enum.map(fn language ->
|
|> Enum.map(fn language ->
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ defmodule BDS.Generation do
|
|||||||
import BDS.Generation.Progress
|
import BDS.Generation.Progress
|
||||||
import BDS.Generation.Outputs
|
import BDS.Generation.Outputs
|
||||||
import BDS.Generation.Data
|
import BDS.Generation.Data
|
||||||
|
import BDS.Generation.Renderers, only: [build_list_posts: 3]
|
||||||
import BDS.Generation.Validation
|
import BDS.Generation.Validation
|
||||||
|
|
||||||
alias BDS.Generation.GeneratedFileHash
|
alias BDS.Generation.GeneratedFileHash
|
||||||
@@ -227,62 +228,127 @@ defmodule BDS.Generation do
|
|||||||
apply_validation(project_id, report, [])
|
apply_validation(project_id, report, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec prepare_validation_apply(String.t(), map(), generation_opts()) ::
|
||||||
|
{:ok, map()} | {:error, term()}
|
||||||
|
def prepare_validation_apply(project_id, report, opts \\ [])
|
||||||
|
|
||||||
|
def prepare_validation_apply(project_id, report, opts)
|
||||||
|
when is_binary(project_id) and is_map(report) and is_list(opts) do
|
||||||
|
on_progress = callback(opts)
|
||||||
|
:ok = report_validation_progress(on_progress, 0.0, "Planning validation apply steps...")
|
||||||
|
|
||||||
|
with {:ok, apply_plan} <- validation_apply_plan(project_id, @core_sections, report) do
|
||||||
|
sections_to_render = validation_apply_sections_to_render(apply_plan.targeted_plan)
|
||||||
|
project = Projects.get_project!(project_id)
|
||||||
|
|
||||||
|
:ok = report_validation_progress(on_progress, 0.2, "Deleting extra URLs...")
|
||||||
|
|
||||||
|
{deleted_url_count, removed_empty_dir_count} =
|
||||||
|
delete_extra_validation_paths(
|
||||||
|
project_id,
|
||||||
|
project,
|
||||||
|
Map.get(report, :extra_url_paths, []),
|
||||||
|
on_progress
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok =
|
||||||
|
report_validation_progress(
|
||||||
|
on_progress,
|
||||||
|
1.0,
|
||||||
|
"Validation apply prepared"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
sections_to_render: sections_to_render,
|
||||||
|
deleted_url_count: deleted_url_count,
|
||||||
|
removed_empty_dir_count: removed_empty_dir_count
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec apply_validation_section(String.t(), map(), section(), generation_opts()) ::
|
||||||
|
{:ok, map()} | {:error, term()}
|
||||||
|
def apply_validation_section(project_id, report, section, opts \\ [])
|
||||||
|
|
||||||
|
def apply_validation_section(project_id, report, section, opts)
|
||||||
|
when is_binary(project_id) and is_map(report) and section in @core_sections and
|
||||||
|
is_list(opts) do
|
||||||
|
on_progress = callback(opts)
|
||||||
|
:ok = report_validation_progress(on_progress, 0.0, validation_section_task_label(section))
|
||||||
|
|
||||||
|
with {:ok, apply_plan} <- validation_apply_plan(project_id, [section], report) do
|
||||||
|
outputs_to_render =
|
||||||
|
targeted_apply_outputs(apply_plan.plan, apply_plan.targeted_plan, [section])
|
||||||
|
|
||||||
|
rendered_url_count = write_validation_outputs(project_id, outputs_to_render, on_progress)
|
||||||
|
|
||||||
|
{:ok, %{section: section, rendered_url_count: rendered_url_count}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec refresh_validation_ancillary_outputs(String.t(), :calendar, generation_opts()) ::
|
||||||
|
{:ok, map()} | {:error, term()}
|
||||||
|
def refresh_validation_ancillary_outputs(project_id, kind, opts \\ [])
|
||||||
|
|
||||||
|
def refresh_validation_ancillary_outputs(project_id, kind, opts)
|
||||||
|
when is_binary(project_id) and kind in [:calendar] and is_list(opts) do
|
||||||
|
on_progress = callback(opts)
|
||||||
|
:ok = report_validation_progress(on_progress, 0.0, ancillary_validation_task_label(kind))
|
||||||
|
|
||||||
|
with {:ok, plan} <- plan_generation(project_id, @core_sections) do
|
||||||
|
expected_output_map = refresh_validation_output_map(plan, kind, on_progress)
|
||||||
|
paths = ancillary_validation_paths(expected_output_map, kind)
|
||||||
|
label = ancillary_validation_progress_label(kind)
|
||||||
|
total = length(paths)
|
||||||
|
|
||||||
|
:ok = report_generation_started(on_progress, total, label)
|
||||||
|
|
||||||
|
paths
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.each(fn {relative_path, index} ->
|
||||||
|
_ =
|
||||||
|
write_generated_file(
|
||||||
|
project_id,
|
||||||
|
relative_path,
|
||||||
|
Map.fetch!(expected_output_map, relative_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
:ok = report_generation_progress(on_progress, index, total, label)
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, %{kind: kind, written_count: length(paths)}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp refresh_validation_output_map(plan, :calendar, _on_progress) do
|
||||||
|
data = generation_data(plan)
|
||||||
|
%{"calendar.json" => BDS.Generation.Sitemap.render_calendar(data.published_list_posts)}
|
||||||
|
end
|
||||||
|
|
||||||
@spec apply_validation(String.t(), map(), generation_opts()) :: {:ok, map()} | {:error, term()}
|
@spec apply_validation(String.t(), map(), generation_opts()) :: {:ok, map()} | {:error, term()}
|
||||||
def apply_validation(project_id, report, opts)
|
def apply_validation(project_id, report, opts)
|
||||||
when is_binary(project_id) and is_map(report) and is_list(opts) do
|
when is_binary(project_id) and is_map(report) and is_list(opts) do
|
||||||
on_progress = callback(opts)
|
on_progress = callback(opts)
|
||||||
|
|
||||||
with {:ok, plan} <- plan_generation(project_id, @core_sections) do
|
with {:ok, apply_plan} <- validation_apply_plan(project_id, @core_sections, report) do
|
||||||
expected_outputs = build_outputs(plan)
|
plan = apply_plan.plan
|
||||||
expected_output_map = Map.new(expected_outputs)
|
|
||||||
project = Projects.get_project!(project_id)
|
project = Projects.get_project!(project_id)
|
||||||
published_posts = list_published_posts(project_id)
|
|
||||||
|
|
||||||
targeted_plan =
|
outputs_to_render = targeted_apply_outputs(plan, apply_plan.targeted_plan, plan.sections)
|
||||||
build_targeted_validation_plan(
|
|
||||||
plan_validation_paths(report_paths(report), additional_languages(plan)),
|
|
||||||
published_posts
|
|
||||||
)
|
|
||||||
|
|
||||||
outputs_to_render =
|
rendered_url_count = write_validation_outputs(project_id, outputs_to_render, on_progress)
|
||||||
expected_outputs
|
|
||||||
|> Enum.filter(fn {relative_path, _content} ->
|
|
||||||
targeted_output?(
|
|
||||||
relative_path,
|
|
||||||
targeted_plan,
|
|
||||||
plan.language,
|
|
||||||
additional_languages(plan)
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
|
|
||||||
total_to_render = length(outputs_to_render)
|
|
||||||
:ok = report_generation_started(on_progress, total_to_render, "validation routes")
|
|
||||||
|
|
||||||
outputs_to_render
|
|
||||||
|> Enum.with_index(1)
|
|
||||||
|> Enum.each(fn {{relative_path, content}, index} ->
|
|
||||||
_ =
|
|
||||||
write_generated_file(project_id, relative_path, content,
|
|
||||||
refresh_timestamp_on_unchanged: route_html_path?(relative_path)
|
|
||||||
)
|
|
||||||
|
|
||||||
:ok =
|
|
||||||
report_generation_progress(on_progress, index, total_to_render, "validation routes")
|
|
||||||
end)
|
|
||||||
|
|
||||||
{deleted_url_count, removed_empty_dir_count} =
|
{deleted_url_count, removed_empty_dir_count} =
|
||||||
delete_extra_validation_paths(project_id, project, Map.get(report, :extra_url_paths, []))
|
delete_extra_validation_paths(project_id, project, Map.get(report, :extra_url_paths, []))
|
||||||
|
|
||||||
if outputs_to_render != [] or deleted_url_count > 0 do
|
if outputs_to_render != [] or deleted_url_count > 0 do
|
||||||
write_ancillary_validation_outputs(project_id, expected_output_map)
|
{:ok, _} = refresh_validation_ancillary_outputs(project_id, :calendar)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
%{
|
%{
|
||||||
rendered_url_count:
|
rendered_url_count: rendered_url_count,
|
||||||
Enum.count(outputs_to_render, fn {relative_path, _content} ->
|
|
||||||
route_html_path?(relative_path)
|
|
||||||
end),
|
|
||||||
deleted_url_count: deleted_url_count,
|
deleted_url_count: deleted_url_count,
|
||||||
removed_empty_dir_count: removed_empty_dir_count
|
removed_empty_dir_count: removed_empty_dir_count
|
||||||
}}
|
}}
|
||||||
@@ -372,7 +438,7 @@ defmodule BDS.Generation do
|
|||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_outputs(plan) do
|
defp build_outputs(plan, on_output \\ nil) do
|
||||||
data = generation_data(plan)
|
data = generation_data(plan)
|
||||||
published_translations = flattened_generation_translations(data.translations_by_post)
|
published_translations = flattened_generation_translations(data.translations_by_post)
|
||||||
translations_by_post_language = translation_lookup_map(published_translations)
|
translations_by_post_language = translation_lookup_map(published_translations)
|
||||||
@@ -419,7 +485,8 @@ defmodule BDS.Generation do
|
|||||||
build_core_outputs(
|
build_core_outputs(
|
||||||
plan,
|
plan,
|
||||||
data.published_list_posts,
|
data.published_list_posts,
|
||||||
localized_list_posts_by_language
|
localized_list_posts_by_language,
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
@@ -432,7 +499,8 @@ defmodule BDS.Generation do
|
|||||||
plan.language,
|
plan.language,
|
||||||
data.published_posts,
|
data.published_posts,
|
||||||
translations_by_post_language,
|
translations_by_post_language,
|
||||||
localized_posts_by_language
|
localized_posts_by_language,
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
@@ -445,14 +513,15 @@ defmodule BDS.Generation do
|
|||||||
plan.language,
|
plan.language,
|
||||||
data.published_posts,
|
data.published_posts,
|
||||||
translations_by_post_language,
|
translations_by_post_language,
|
||||||
localized_posts_by_language
|
localized_posts_by_language,
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
archive_outputs =
|
archive_outputs =
|
||||||
build_archive_outputs(plan, data.post_index, localized_post_indexes)
|
build_archive_outputs(plan, data.post_index, localized_post_indexes, on_output)
|
||||||
|
|
||||||
urls =
|
urls =
|
||||||
(core_outputs ++ page_outputs ++ single_outputs ++ archive_outputs)
|
(core_outputs ++ page_outputs ++ single_outputs ++ archive_outputs)
|
||||||
@@ -606,6 +675,346 @@ defmodule BDS.Generation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validation_apply_plan(project_id, sections, report) do
|
||||||
|
with {:ok, plan} <- plan_generation(project_id, sections) do
|
||||||
|
published_posts = list_published_posts(project_id)
|
||||||
|
|
||||||
|
targeted_plan =
|
||||||
|
build_targeted_validation_plan(
|
||||||
|
plan_validation_paths(report_paths(report), additional_languages(plan)),
|
||||||
|
published_posts
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, %{plan: plan, targeted_plan: targeted_plan}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Render ONLY the routes targeted by the validation report for the given
|
||||||
|
# sections, matching the old app's behaviour of filtering the render inputs
|
||||||
|
# rather than rendering the whole site and discarding the surplus. Falls back
|
||||||
|
# to a full section render only when a report path could not be classified.
|
||||||
|
defp targeted_apply_outputs(plan, targeted_plan, sections) do
|
||||||
|
if fallback_validation_section_render?(targeted_plan) do
|
||||||
|
Enum.filter(build_outputs(plan), fn {relative_path, _content} ->
|
||||||
|
path_section(relative_path) in sections
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
render_data = validation_render_data(plan)
|
||||||
|
|
||||||
|
Enum.flat_map(sections, fn section ->
|
||||||
|
build_targeted_section_outputs(plan, render_data, targeted_plan, section)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write the targeted outputs, streaming one progress message per rewritten URL
|
||||||
|
# (exactly like the old app's `Generated /url` reporting) so the task shows the
|
||||||
|
# actual URLs and advances by the number of URLs rewritten.
|
||||||
|
defp write_validation_outputs(project_id, outputs, on_progress) do
|
||||||
|
route_total = Enum.count(outputs, fn {relative_path, _content} -> route_html_path?(relative_path) end)
|
||||||
|
|
||||||
|
:ok = report_validation_progress(on_progress, 0.0, validation_rewrite_started_message(route_total))
|
||||||
|
|
||||||
|
{rendered, _total} =
|
||||||
|
Enum.reduce(outputs, {0, route_total}, fn {relative_path, content}, {rendered, total} ->
|
||||||
|
_ =
|
||||||
|
write_generated_file(project_id, relative_path, content,
|
||||||
|
refresh_timestamp_on_unchanged: route_html_path?(relative_path)
|
||||||
|
)
|
||||||
|
|
||||||
|
if route_html_path?(relative_path) do
|
||||||
|
rewritten = rendered + 1
|
||||||
|
progress = if total > 0, do: rewritten / total, else: 1.0
|
||||||
|
|
||||||
|
:ok =
|
||||||
|
report_validation_progress(
|
||||||
|
on_progress,
|
||||||
|
progress,
|
||||||
|
"Rewrote #{relative_path_to_url_path(relative_path)} (#{rewritten}/#{total})"
|
||||||
|
)
|
||||||
|
|
||||||
|
{rewritten, total}
|
||||||
|
else
|
||||||
|
{rendered, total}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
rendered
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_rewrite_started_message(0), do: "No URLs to rewrite"
|
||||||
|
defp validation_rewrite_started_message(1), do: "Rewriting 1 URL..."
|
||||||
|
defp validation_rewrite_started_message(total), do: "Rewriting #{total} URLs..."
|
||||||
|
|
||||||
|
defp validation_render_data(plan) do
|
||||||
|
data = generation_data(plan)
|
||||||
|
|
||||||
|
translations_by_post_language =
|
||||||
|
data.translations_by_post
|
||||||
|
|> flattened_generation_translations()
|
||||||
|
|> translation_lookup_map()
|
||||||
|
|
||||||
|
translatable_posts =
|
||||||
|
Enum.reject(data.published_posts, &truthy_flag?(Map.get(&1, :do_not_translate)))
|
||||||
|
|
||||||
|
translatable_list_posts =
|
||||||
|
Enum.reject(data.published_list_posts, &truthy_flag?(Map.get(&1, :do_not_translate)))
|
||||||
|
|
||||||
|
localized_posts_by_language =
|
||||||
|
Map.new(additional_languages(plan), fn language ->
|
||||||
|
{language,
|
||||||
|
resolve_posts_for_language(
|
||||||
|
translatable_posts,
|
||||||
|
language,
|
||||||
|
translations_by_post_language,
|
||||||
|
plan.language
|
||||||
|
)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
localized_list_posts_by_language =
|
||||||
|
Map.new(additional_languages(plan), fn language ->
|
||||||
|
{language,
|
||||||
|
resolve_posts_for_language(
|
||||||
|
translatable_list_posts,
|
||||||
|
language,
|
||||||
|
translations_by_post_language,
|
||||||
|
plan.language
|
||||||
|
)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
localized_post_indexes =
|
||||||
|
Map.new(localized_list_posts_by_language, fn {language, posts} ->
|
||||||
|
{language, build_generation_post_index(posts)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{
|
||||||
|
data: data,
|
||||||
|
translations_by_post_language: translations_by_post_language,
|
||||||
|
localized_posts_by_language: localized_posts_by_language,
|
||||||
|
localized_list_posts_by_language: localized_list_posts_by_language,
|
||||||
|
localized_post_indexes: localized_post_indexes
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_section_outputs(plan, render_data, targeted_plan, :core) do
|
||||||
|
data = render_data.data
|
||||||
|
|
||||||
|
main_root =
|
||||||
|
if targeted_plan.request_root_routes do
|
||||||
|
posts = build_list_posts(plan.base_url, data.published_list_posts, nil)
|
||||||
|
build_root_outputs(plan, plan.language, posts)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
language_root =
|
||||||
|
Enum.flat_map(additional_languages(plan), fn language ->
|
||||||
|
language_plan = language_targeted_plan(targeted_plan, language)
|
||||||
|
|
||||||
|
if language_plan.request_root_routes do
|
||||||
|
source = Map.get(render_data.localized_list_posts_by_language, language, [])
|
||||||
|
prefix = route_language(plan.language, language)
|
||||||
|
posts = build_list_posts(plan.base_url, source, prefix)
|
||||||
|
build_root_outputs(plan, language, posts)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
main_page_posts =
|
||||||
|
Enum.filter(
|
||||||
|
data.published_posts,
|
||||||
|
&MapSet.member?(targeted_plan.requested_page_slugs, &1.slug)
|
||||||
|
)
|
||||||
|
|
||||||
|
localized_page_posts =
|
||||||
|
Map.new(additional_languages(plan), fn language ->
|
||||||
|
language_plan = language_targeted_plan(targeted_plan, language)
|
||||||
|
source = Map.get(render_data.localized_posts_by_language, language, [])
|
||||||
|
{language, Enum.filter(source, &MapSet.member?(language_plan.requested_page_slugs, &1.slug))}
|
||||||
|
end)
|
||||||
|
|
||||||
|
page_outputs =
|
||||||
|
build_page_outputs(
|
||||||
|
plan.project_id,
|
||||||
|
plan.language,
|
||||||
|
main_page_posts,
|
||||||
|
render_data.translations_by_post_language,
|
||||||
|
localized_page_posts
|
||||||
|
)
|
||||||
|
|
||||||
|
main_root ++ language_root ++ page_outputs
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_section_outputs(plan, render_data, targeted_plan, :single) do
|
||||||
|
data = render_data.data
|
||||||
|
|
||||||
|
main_posts =
|
||||||
|
filter_route_posts(data.published_posts, targeted_plan.requested_post_routes)
|
||||||
|
|
||||||
|
localized_posts =
|
||||||
|
Map.new(additional_languages(plan), fn language ->
|
||||||
|
language_plan = language_targeted_plan(targeted_plan, language)
|
||||||
|
source = Map.get(render_data.localized_posts_by_language, language, [])
|
||||||
|
{language, filter_route_posts(source, language_plan.requested_post_routes)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
build_single_outputs(
|
||||||
|
plan.project_id,
|
||||||
|
plan.language,
|
||||||
|
main_posts,
|
||||||
|
render_data.translations_by_post_language,
|
||||||
|
localized_posts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_section_outputs(plan, render_data, targeted_plan, :category) do
|
||||||
|
build_targeted_archive_outputs(plan, render_data, targeted_plan, fn plan_arg,
|
||||||
|
index,
|
||||||
|
language_plan,
|
||||||
|
languages ->
|
||||||
|
build_category_outputs(
|
||||||
|
plan_arg,
|
||||||
|
filter_collection_by_segment(index.posts_by_category, language_plan.requested_category_slugs),
|
||||||
|
languages
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_section_outputs(plan, render_data, targeted_plan, :tag) do
|
||||||
|
build_targeted_archive_outputs(plan, render_data, targeted_plan, fn plan_arg,
|
||||||
|
index,
|
||||||
|
language_plan,
|
||||||
|
languages ->
|
||||||
|
build_tag_outputs(
|
||||||
|
plan_arg,
|
||||||
|
filter_collection_by_segment(index.posts_by_tag, language_plan.requested_tag_slugs),
|
||||||
|
languages
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_section_outputs(plan, render_data, targeted_plan, :date) do
|
||||||
|
build_targeted_archive_outputs(plan, render_data, targeted_plan, fn plan_arg,
|
||||||
|
index,
|
||||||
|
language_plan,
|
||||||
|
languages ->
|
||||||
|
build_date_outputs(plan_arg, filter_date_index(index, language_plan), languages)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_targeted_archive_outputs(plan, render_data, targeted_plan, builder) do
|
||||||
|
main_outputs = builder.(plan, render_data.data.post_index, targeted_plan, [plan.language])
|
||||||
|
|
||||||
|
language_outputs =
|
||||||
|
Enum.flat_map(additional_languages(plan), fn language ->
|
||||||
|
language_plan = language_targeted_plan(targeted_plan, language)
|
||||||
|
index = Map.get(render_data.localized_post_indexes, language, empty_generation_post_index())
|
||||||
|
builder.(plan, index, language_plan, [language])
|
||||||
|
end)
|
||||||
|
|
||||||
|
main_outputs ++ language_outputs
|
||||||
|
end
|
||||||
|
|
||||||
|
defp language_targeted_plan(targeted_plan, language) do
|
||||||
|
Map.get(targeted_plan.language_plans, language, empty_validation_path_plan())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filter_route_posts(posts, route_keys) do
|
||||||
|
Enum.filter(posts, fn post ->
|
||||||
|
{year, month, day} = local_date_parts!(post.created_at)
|
||||||
|
MapSet.member?(route_keys, route_key(year, month, day, post.slug))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filter_collection_by_segment(collection, segment_slugs) do
|
||||||
|
collection
|
||||||
|
|> Enum.filter(fn {key, _posts} ->
|
||||||
|
MapSet.member?(segment_slugs, archive_route_segment(key))
|
||||||
|
end)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filter_date_index(index, language_plan) do
|
||||||
|
%{
|
||||||
|
posts_by_year: filter_map_by_keys(index.posts_by_year, language_plan.requested_years),
|
||||||
|
posts_by_year_month:
|
||||||
|
filter_map_by_keys(index.posts_by_year_month, language_plan.requested_year_months),
|
||||||
|
posts_by_year_month_day:
|
||||||
|
filter_map_by_keys(index.posts_by_year_month_day, language_plan.requested_year_month_days)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filter_map_by_keys(map, keys) do
|
||||||
|
map
|
||||||
|
|> Enum.filter(fn {key, _value} -> MapSet.member?(keys, key) end)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp empty_generation_post_index do
|
||||||
|
%{
|
||||||
|
posts_by_category: %{},
|
||||||
|
posts_by_tag: %{},
|
||||||
|
posts_by_year: %{},
|
||||||
|
posts_by_year_month: %{},
|
||||||
|
posts_by_year_month_day: %{}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_apply_sections_to_render(targeted_plan) do
|
||||||
|
if fallback_validation_section_render?(targeted_plan) do
|
||||||
|
[:category, :tag, :date, :core, :single]
|
||||||
|
else
|
||||||
|
targeted_plan
|
||||||
|
|> validation_apply_section_plans()
|
||||||
|
|> Enum.reduce([], &append_validation_apply_sections/2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fallback_validation_section_render?(%{requires_fallback_section_render: true}), do: true
|
||||||
|
|
||||||
|
defp fallback_validation_section_render?(%{language_plans: language_plans}) do
|
||||||
|
Enum.any?(Map.values(language_plans), &fallback_validation_section_render?/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_apply_section_plans(targeted_plan) do
|
||||||
|
[targeted_plan | Map.values(Map.get(targeted_plan, :language_plans, %{}))]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp append_validation_apply_sections(plan, sections) do
|
||||||
|
sections
|
||||||
|
|> maybe_append_validation_apply_section(:core, validation_apply_core_section?(plan))
|
||||||
|
|> maybe_append_validation_apply_section(
|
||||||
|
:single,
|
||||||
|
not MapSet.equal?(plan.requested_post_routes, MapSet.new())
|
||||||
|
)
|
||||||
|
|> maybe_append_validation_apply_section(
|
||||||
|
:category,
|
||||||
|
not MapSet.equal?(plan.requested_category_slugs, MapSet.new())
|
||||||
|
)
|
||||||
|
|> maybe_append_validation_apply_section(
|
||||||
|
:tag,
|
||||||
|
not MapSet.equal?(plan.requested_tag_slugs, MapSet.new())
|
||||||
|
)
|
||||||
|
|> maybe_append_validation_apply_section(:date, validation_apply_date_section?(plan))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_apply_core_section?(plan) do
|
||||||
|
plan.request_root_routes or not MapSet.equal?(plan.requested_page_slugs, MapSet.new())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validation_apply_date_section?(plan) do
|
||||||
|
not MapSet.equal?(plan.requested_years, MapSet.new()) or
|
||||||
|
not MapSet.equal?(plan.requested_year_months, MapSet.new()) or
|
||||||
|
not MapSet.equal?(plan.requested_year_month_days, MapSet.new())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_append_validation_apply_section(sections, _section, false), do: sections
|
||||||
|
|
||||||
|
defp maybe_append_validation_apply_section(sections, section, true) do
|
||||||
|
if section in sections, do: sections, else: sections ++ [section]
|
||||||
|
end
|
||||||
|
|
||||||
defp path_section(relative_path) do
|
defp path_section(relative_path) do
|
||||||
segments = String.split(relative_path, "/", trim: true)
|
segments = String.split(relative_path, "/", trim: true)
|
||||||
|
|
||||||
@@ -698,11 +1107,34 @@ defmodule BDS.Generation do
|
|||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validation_section_task_label(:core), do: "Render Site Core"
|
||||||
|
defp validation_section_task_label(:single), do: "Render Single Posts"
|
||||||
|
defp validation_section_task_label(:category), do: "Render Category Archives"
|
||||||
|
defp validation_section_task_label(:tag), do: "Render Tag Archives"
|
||||||
|
defp validation_section_task_label(:date), do: "Render Date Archives"
|
||||||
|
|
||||||
|
defp ancillary_validation_progress_label(:calendar), do: "calendar files"
|
||||||
|
|
||||||
|
defp ancillary_validation_task_label(:calendar), do: "Regenerate Calendar"
|
||||||
|
|
||||||
defp delete_extra_validation_paths(project_id, project, extra_url_paths) do
|
defp delete_extra_validation_paths(project_id, project, extra_url_paths) do
|
||||||
Enum.reduce(extra_url_paths, {0, 0}, fn url_path, {deleted_count, removed_dir_count} ->
|
delete_extra_validation_paths(project_id, project, extra_url_paths, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_extra_validation_paths(project_id, project, extra_url_paths, on_progress) do
|
||||||
|
total = length(extra_url_paths)
|
||||||
|
|
||||||
|
if total == 0 do
|
||||||
|
:ok = report_validation_progress(on_progress, 0.5, "No extra URLs to delete")
|
||||||
|
end
|
||||||
|
|
||||||
|
extra_url_paths
|
||||||
|
|> Enum.with_index(1)
|
||||||
|
|> Enum.reduce({0, 0}, fn {url_path, index}, {deleted_count, removed_dir_count} ->
|
||||||
relative_path = url_path_to_relative_index_path(url_path)
|
relative_path = url_path_to_relative_index_path(url_path)
|
||||||
full_path = output_path(project, relative_path)
|
full_path = output_path(project, relative_path)
|
||||||
|
|
||||||
|
result =
|
||||||
case File.rm(full_path) do
|
case File.rm(full_path) do
|
||||||
:ok ->
|
:ok ->
|
||||||
Repo.delete_all(
|
Repo.delete_all(
|
||||||
@@ -723,25 +1155,22 @@ defmodule BDS.Generation do
|
|||||||
{:error, _reason} ->
|
{:error, _reason} ->
|
||||||
{deleted_count, removed_dir_count}
|
{deleted_count, removed_dir_count}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
:ok =
|
||||||
|
report_validation_progress(
|
||||||
|
on_progress,
|
||||||
|
0.2 + 0.3 * index / max(total, 1),
|
||||||
|
"Deleted #{index}/#{total} extra URLs"
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp write_ancillary_validation_outputs(project_id, expected_output_map) do
|
defp ancillary_validation_paths(expected_output_map, :calendar) do
|
||||||
ancillary_paths =
|
expected_output_map
|
||||||
Enum.filter(Map.keys(expected_output_map), fn relative_path ->
|
|> Map.keys()
|
||||||
relative_path == "calendar.json" or String.contains?(relative_path, "pagefind/")
|
|> Enum.filter(&(&1 == "calendar.json"))
|
||||||
end)
|
|
||||||
|
|
||||||
Enum.each(ancillary_paths, fn relative_path ->
|
|
||||||
_ =
|
|
||||||
write_generated_file(
|
|
||||||
project_id,
|
|
||||||
relative_path,
|
|
||||||
Map.fetch!(expected_output_map, relative_path)
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp output_path(project, relative_path) do
|
defp output_path(project, relative_path) do
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ defmodule BDS.Generation.Outputs do
|
|||||||
|
|
||||||
alias BDS.Rendering.TemplateSelection
|
alias BDS.Rendering.TemplateSelection
|
||||||
|
|
||||||
|
@type output_callback :: ({String.t(), iodata()} -> any()) | nil
|
||||||
|
|
||||||
@spec additional_languages(map()) :: [String.t()]
|
@spec additional_languages(map()) :: [String.t()]
|
||||||
def additional_languages(plan) do
|
def additional_languages(plan) do
|
||||||
Enum.reject(plan.blog_languages, &(&1 == plan.language))
|
Enum.reject(plan.blog_languages, &(&1 == plan.language))
|
||||||
@@ -149,16 +151,17 @@ defmodule BDS.Generation.Outputs do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_archive_outputs(map(), map(), map()) :: [{String.t(), iodata()}]
|
@spec build_archive_outputs(map(), map(), map(), output_callback()) :: [{String.t(), iodata()}]
|
||||||
def build_archive_outputs(plan, post_index, localized_post_indexes) do
|
def build_archive_outputs(plan, post_index, localized_post_indexes, on_output \\ nil) do
|
||||||
category_outputs =
|
category_outputs =
|
||||||
if :category in plan.sections do
|
if :category in plan.sections do
|
||||||
build_category_outputs(plan, post_index.posts_by_category, [plan.language]) ++
|
build_category_outputs(plan, post_index.posts_by_category, [plan.language], on_output) ++
|
||||||
Enum.flat_map(additional_languages(plan), fn language ->
|
Enum.flat_map(additional_languages(plan), fn language ->
|
||||||
build_category_outputs(
|
build_category_outputs(
|
||||||
plan,
|
plan,
|
||||||
Map.get(localized_post_indexes, language, %{posts_by_category: %{}}).posts_by_category,
|
Map.get(localized_post_indexes, language, %{posts_by_category: %{}}).posts_by_category,
|
||||||
[language]
|
[language],
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
@@ -167,12 +170,13 @@ defmodule BDS.Generation.Outputs do
|
|||||||
|
|
||||||
tag_outputs =
|
tag_outputs =
|
||||||
if :tag in plan.sections do
|
if :tag in plan.sections do
|
||||||
build_tag_outputs(plan, post_index.posts_by_tag, [plan.language]) ++
|
build_tag_outputs(plan, post_index.posts_by_tag, [plan.language], on_output) ++
|
||||||
Enum.flat_map(additional_languages(plan), fn language ->
|
Enum.flat_map(additional_languages(plan), fn language ->
|
||||||
build_tag_outputs(
|
build_tag_outputs(
|
||||||
plan,
|
plan,
|
||||||
Map.get(localized_post_indexes, language, %{posts_by_tag: %{}}).posts_by_tag,
|
Map.get(localized_post_indexes, language, %{posts_by_tag: %{}}).posts_by_tag,
|
||||||
[language]
|
[language],
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
@@ -181,7 +185,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
|
|
||||||
date_outputs =
|
date_outputs =
|
||||||
if :date in plan.sections do
|
if :date in plan.sections do
|
||||||
build_date_outputs(plan, post_index, [plan.language]) ++
|
build_date_outputs(plan, post_index, [plan.language], on_output) ++
|
||||||
Enum.flat_map(additional_languages(plan), fn language ->
|
Enum.flat_map(additional_languages(plan), fn language ->
|
||||||
build_date_outputs(
|
build_date_outputs(
|
||||||
plan,
|
plan,
|
||||||
@@ -190,7 +194,8 @@ defmodule BDS.Generation.Outputs do
|
|||||||
language,
|
language,
|
||||||
%{posts_by_year: %{}, posts_by_year_month: %{}, posts_by_year_month_day: %{}}
|
%{posts_by_year: %{}, posts_by_year_month: %{}, posts_by_year_month_day: %{}}
|
||||||
),
|
),
|
||||||
[language]
|
[language],
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
@@ -200,8 +205,10 @@ defmodule BDS.Generation.Outputs do
|
|||||||
category_outputs ++ tag_outputs ++ date_outputs
|
category_outputs ++ tag_outputs ++ date_outputs
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_category_outputs(map(), map(), [String.t()]) :: [{String.t(), iodata()}]
|
@spec build_category_outputs(map(), map(), [String.t()], output_callback()) :: [
|
||||||
def build_category_outputs(plan, posts_by_category, languages) do
|
{String.t(), iodata()}
|
||||||
|
]
|
||||||
|
def build_category_outputs(plan, posts_by_category, languages, on_output \\ nil) do
|
||||||
Enum.flat_map(posts_by_category, fn {category, posts} ->
|
Enum.flat_map(posts_by_category, fn {category, posts} ->
|
||||||
paginated_posts = Enum.chunk_every(posts, max(plan.max_posts_per_page, 1))
|
paginated_posts = Enum.chunk_every(posts, max(plan.max_posts_per_page, 1))
|
||||||
category_slug = archive_route_segment(category)
|
category_slug = archive_route_segment(category)
|
||||||
@@ -240,6 +247,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output =
|
||||||
{
|
{
|
||||||
archive_path(
|
archive_path(
|
||||||
route_language(plan.language, language),
|
route_language(plan.language, language),
|
||||||
@@ -248,26 +256,37 @@ defmodule BDS.Generation.Outputs do
|
|||||||
),
|
),
|
||||||
render_archive_page(plan, category, page_posts, language, "category", pagination)
|
render_archive_page(plan, category, page_posts, language, "category", pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_tag_outputs(map(), map(), [String.t()]) :: [{String.t(), iodata()}]
|
@spec build_tag_outputs(map(), map(), [String.t()], output_callback()) :: [
|
||||||
def build_tag_outputs(plan, posts_by_tag, languages) do
|
{String.t(), iodata()}
|
||||||
|
]
|
||||||
|
def build_tag_outputs(plan, posts_by_tag, languages, on_output \\ nil) do
|
||||||
Enum.flat_map(posts_by_tag, fn {tag, posts} ->
|
Enum.flat_map(posts_by_tag, fn {tag, posts} ->
|
||||||
tag_slug = archive_route_segment(tag)
|
tag_slug = archive_route_segment(tag)
|
||||||
|
|
||||||
build_paginated_archive_outputs(plan, languages, ["tag", tag_slug], posts, fn page_posts,
|
build_paginated_archive_outputs(
|
||||||
language,
|
plan,
|
||||||
pagination ->
|
languages,
|
||||||
|
["tag", tag_slug],
|
||||||
|
posts,
|
||||||
|
fn page_posts, language, pagination ->
|
||||||
render_archive_page(plan, tag, page_posts, language, "tag", pagination)
|
render_archive_page(plan, tag, page_posts, language, "tag", pagination)
|
||||||
end)
|
end,
|
||||||
|
on_output
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_date_outputs(map(), map(), [String.t()]) :: [{String.t(), iodata()}]
|
@spec build_date_outputs(map(), map(), [String.t()], output_callback()) :: [
|
||||||
def build_date_outputs(plan, post_index, languages) do
|
{String.t(), iodata()}
|
||||||
|
]
|
||||||
|
def build_date_outputs(plan, post_index, languages, on_output \\ nil) do
|
||||||
year_outputs =
|
year_outputs =
|
||||||
Enum.flat_map(post_index.posts_by_year, fn {year, posts} ->
|
Enum.flat_map(post_index.posts_by_year, fn {year, posts} ->
|
||||||
build_paginated_archive_outputs(
|
build_paginated_archive_outputs(
|
||||||
@@ -284,7 +303,8 @@ defmodule BDS.Generation.Outputs do
|
|||||||
language,
|
language,
|
||||||
pagination
|
pagination
|
||||||
)
|
)
|
||||||
end
|
end,
|
||||||
|
on_output
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -292,9 +312,12 @@ defmodule BDS.Generation.Outputs do
|
|||||||
Enum.flat_map(post_index.posts_by_year_month, fn {year_month, posts} ->
|
Enum.flat_map(post_index.posts_by_year_month, fn {year_month, posts} ->
|
||||||
[year, month] = String.split(year_month, "/", parts: 2)
|
[year, month] = String.split(year_month, "/", parts: 2)
|
||||||
|
|
||||||
build_paginated_archive_outputs(plan, languages, [year, month], posts, fn page_posts,
|
build_paginated_archive_outputs(
|
||||||
language,
|
plan,
|
||||||
pagination ->
|
languages,
|
||||||
|
[year, month],
|
||||||
|
posts,
|
||||||
|
fn page_posts, language, pagination ->
|
||||||
render_date_archive_page(
|
render_date_archive_page(
|
||||||
plan,
|
plan,
|
||||||
"#{year}-#{month}",
|
"#{year}-#{month}",
|
||||||
@@ -303,16 +326,21 @@ defmodule BDS.Generation.Outputs do
|
|||||||
language,
|
language,
|
||||||
pagination
|
pagination
|
||||||
)
|
)
|
||||||
end)
|
end,
|
||||||
|
on_output
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
day_outputs =
|
day_outputs =
|
||||||
Enum.flat_map(post_index.posts_by_year_month_day, fn {year_month_day, posts} ->
|
Enum.flat_map(post_index.posts_by_year_month_day, fn {year_month_day, posts} ->
|
||||||
[year, month, day] = String.split(year_month_day, "/", parts: 3)
|
[year, month, day] = String.split(year_month_day, "/", parts: 3)
|
||||||
|
|
||||||
build_paginated_archive_outputs(plan, languages, [year, month, day], posts, fn page_posts,
|
build_paginated_archive_outputs(
|
||||||
language,
|
plan,
|
||||||
pagination ->
|
languages,
|
||||||
|
[year, month, day],
|
||||||
|
posts,
|
||||||
|
fn page_posts, language, pagination ->
|
||||||
render_date_archive_page(
|
render_date_archive_page(
|
||||||
plan,
|
plan,
|
||||||
"#{year}-#{month}-#{day}",
|
"#{year}-#{month}-#{day}",
|
||||||
@@ -326,25 +354,31 @@ defmodule BDS.Generation.Outputs do
|
|||||||
language,
|
language,
|
||||||
pagination
|
pagination
|
||||||
)
|
)
|
||||||
end)
|
end,
|
||||||
|
on_output
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
year_outputs ++ month_outputs ++ day_outputs
|
year_outputs ++ month_outputs ++ day_outputs
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_core_outputs(map(), [map()], map()) :: [{String.t(), iodata()}]
|
@spec build_core_outputs(map(), [map()], map(), output_callback()) :: [{String.t(), iodata()}]
|
||||||
def build_core_outputs(plan, published_posts, localized_posts_by_language) do
|
def build_core_outputs(plan, published_posts, localized_posts_by_language, on_output \\ nil) do
|
||||||
language = plan.language
|
language = plan.language
|
||||||
additional_languages = Enum.reject(plan.blog_languages, &(&1 == language))
|
additional_languages = Enum.reject(plan.blog_languages, &(&1 == language))
|
||||||
main_posts = build_list_posts(plan.base_url, published_posts, nil)
|
main_posts = build_list_posts(plan.base_url, published_posts, nil)
|
||||||
|
|
||||||
build_root_outputs(plan, language, main_posts) ++
|
main_static_outputs =
|
||||||
[
|
[
|
||||||
{"404.html", render_not_found_output(plan, language)},
|
{"404.html", render_not_found_output(plan, language)},
|
||||||
{"feed.xml", render_feed(plan, language, published_posts)},
|
{"feed.xml", render_feed(plan, language, published_posts)},
|
||||||
{"atom.xml", render_atom(plan, language, published_posts)},
|
{"atom.xml", render_atom(plan, language, published_posts)},
|
||||||
{"calendar.json", render_calendar(published_posts)}
|
{"calendar.json", render_calendar(published_posts)}
|
||||||
] ++
|
]
|
||||||
|
|> Enum.map(&report_output(&1, on_output))
|
||||||
|
|
||||||
|
build_root_outputs(plan, language, main_posts, on_output) ++
|
||||||
|
main_static_outputs ++
|
||||||
Enum.flat_map(additional_languages, fn localized_language ->
|
Enum.flat_map(additional_languages, fn localized_language ->
|
||||||
localized_prefix = route_language(plan.language, localized_language)
|
localized_prefix = route_language(plan.language, localized_language)
|
||||||
localized_source_posts = Map.get(localized_posts_by_language, localized_language, [])
|
localized_source_posts = Map.get(localized_posts_by_language, localized_language, [])
|
||||||
@@ -352,7 +386,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
localized_posts =
|
localized_posts =
|
||||||
build_list_posts(plan.base_url, localized_source_posts, localized_prefix)
|
build_list_posts(plan.base_url, localized_source_posts, localized_prefix)
|
||||||
|
|
||||||
build_root_outputs(plan, localized_language, localized_posts) ++
|
(build_root_outputs(plan, localized_language, localized_posts, on_output) ++
|
||||||
[
|
[
|
||||||
{Path.join(localized_language, "404.html"),
|
{Path.join(localized_language, "404.html"),
|
||||||
render_not_found_output(plan, localized_language)},
|
render_not_found_output(plan, localized_language)},
|
||||||
@@ -360,11 +394,12 @@ defmodule BDS.Generation.Outputs do
|
|||||||
render_feed(plan, localized_language, localized_source_posts)},
|
render_feed(plan, localized_language, localized_source_posts)},
|
||||||
{Path.join(localized_language, "atom.xml"),
|
{Path.join(localized_language, "atom.xml"),
|
||||||
render_atom(plan, localized_language, localized_source_posts)}
|
render_atom(plan, localized_language, localized_source_posts)}
|
||||||
]
|
])
|
||||||
|
|> Enum.map(&report_output(&1, on_output))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_page_outputs(String.t(), String.t(), [map()], map(), map()) :: [
|
@spec build_page_outputs(String.t(), String.t(), [map()], map(), map(), output_callback()) :: [
|
||||||
{String.t(), iodata()}
|
{String.t(), iodata()}
|
||||||
]
|
]
|
||||||
def build_page_outputs(
|
def build_page_outputs(
|
||||||
@@ -372,17 +407,20 @@ defmodule BDS.Generation.Outputs do
|
|||||||
main_language,
|
main_language,
|
||||||
published_posts,
|
published_posts,
|
||||||
translations_by_post_language,
|
translations_by_post_language,
|
||||||
localized_posts_by_language
|
localized_posts_by_language,
|
||||||
|
on_output \\ nil
|
||||||
) do
|
) do
|
||||||
page_outputs =
|
page_outputs =
|
||||||
published_posts
|
published_posts
|
||||||
|> Enum.filter(&("page" in (&1.categories || [])))
|
|> Enum.filter(&("page" in (&1.categories || [])))
|
||||||
|> Enum.map(fn post ->
|
|> Enum.map(fn post ->
|
||||||
canonical_variant = Map.get(translations_by_post_language, {post.id, main_language}, post)
|
canonical_variant = Map.get(translations_by_post_language, {post.id, main_language}, post)
|
||||||
|
render_language = effective_render_language(canonical_variant.language, main_language)
|
||||||
body = load_body(project_id, canonical_variant.file_path, canonical_variant.content)
|
body = load_body(project_id, canonical_variant.file_path, canonical_variant.content)
|
||||||
|
|
||||||
effective_slug = effective_template_slug(project_id, post)
|
effective_slug = effective_template_slug(project_id, post)
|
||||||
|
|
||||||
|
output =
|
||||||
{page_output_path(post.slug, nil),
|
{page_output_path(post.slug, nil),
|
||||||
render_post_output(
|
render_post_output(
|
||||||
project_id,
|
project_id,
|
||||||
@@ -392,7 +430,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
title: canonical_variant.title,
|
title: canonical_variant.title,
|
||||||
content: body,
|
content: body,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
language: canonical_variant.language,
|
language: render_language,
|
||||||
excerpt: canonical_variant.excerpt,
|
excerpt: canonical_variant.excerpt,
|
||||||
_post_record: canonical_variant
|
_post_record: canonical_variant
|
||||||
},
|
},
|
||||||
@@ -401,10 +439,12 @@ defmodule BDS.Generation.Outputs do
|
|||||||
canonical_variant.title,
|
canonical_variant.title,
|
||||||
body,
|
body,
|
||||||
post.slug,
|
post.slug,
|
||||||
canonical_variant.language
|
render_language
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
translation_page_outputs =
|
translation_page_outputs =
|
||||||
@@ -413,10 +453,12 @@ defmodule BDS.Generation.Outputs do
|
|||||||
posts
|
posts
|
||||||
|> Enum.filter(&("page" in (&1.categories || [])))
|
|> Enum.filter(&("page" in (&1.categories || [])))
|
||||||
|> Enum.map(fn post ->
|
|> Enum.map(fn post ->
|
||||||
|
render_language = effective_render_language(post.language, language)
|
||||||
body = load_body(project_id, post.file_path, post.content)
|
body = load_body(project_id, post.file_path, post.content)
|
||||||
|
|
||||||
effective_slug = effective_template_slug(project_id, post)
|
effective_slug = effective_template_slug(project_id, post)
|
||||||
|
|
||||||
|
output =
|
||||||
{page_output_path(post.slug, language),
|
{page_output_path(post.slug, language),
|
||||||
render_post_output(
|
render_post_output(
|
||||||
project_id,
|
project_id,
|
||||||
@@ -426,20 +468,24 @@ defmodule BDS.Generation.Outputs do
|
|||||||
title: post.title,
|
title: post.title,
|
||||||
content: body,
|
content: body,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
language: post.language,
|
language: render_language,
|
||||||
excerpt: post.excerpt,
|
excerpt: post.excerpt,
|
||||||
_post_record: post
|
_post_record: post
|
||||||
},
|
},
|
||||||
fn -> render_post_page(post.title, body, post.slug, post.language) end
|
fn -> render_post_page(post.title, body, post.slug, render_language) end
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
page_outputs ++ translation_page_outputs
|
page_outputs ++ translation_page_outputs
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_root_outputs(map(), String.t(), [map()]) :: [{String.t(), iodata()}]
|
@spec build_root_outputs(map(), String.t(), [map()], output_callback()) :: [
|
||||||
def build_root_outputs(plan, language, posts) do
|
{String.t(), iodata()}
|
||||||
|
]
|
||||||
|
def build_root_outputs(plan, language, posts, on_output \\ nil) do
|
||||||
total_items = length(posts)
|
total_items = length(posts)
|
||||||
total_pages = page_count(total_items, plan.max_posts_per_page)
|
total_pages = page_count(total_items, plan.max_posts_per_page)
|
||||||
|
|
||||||
@@ -449,6 +495,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
|> Enum.map(fn {page_posts, page_number} ->
|
|> Enum.map(fn {page_posts, page_number} ->
|
||||||
route_language = route_language(plan.language, language)
|
route_language = route_language(plan.language, language)
|
||||||
|
|
||||||
|
output =
|
||||||
{root_output_path(route_language, page_number),
|
{root_output_path(route_language, page_number),
|
||||||
render_list_output(
|
render_list_output(
|
||||||
plan,
|
plan,
|
||||||
@@ -466,13 +513,28 @@ defmodule BDS.Generation.Outputs do
|
|||||||
),
|
),
|
||||||
fn -> render_home(plan, language) end
|
fn -> render_home(plan, language) end
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_paginated_archive_outputs(map(), [String.t()], [String.t()], [map()], (... ->
|
@spec build_paginated_archive_outputs(
|
||||||
iodata())) ::
|
map(),
|
||||||
|
[String.t()],
|
||||||
|
[String.t()],
|
||||||
|
[map()],
|
||||||
|
(... -> iodata()),
|
||||||
|
output_callback()
|
||||||
|
) ::
|
||||||
[{String.t(), iodata()}]
|
[{String.t(), iodata()}]
|
||||||
def build_paginated_archive_outputs(plan, languages, segments, posts, render_fun) do
|
def build_paginated_archive_outputs(
|
||||||
|
plan,
|
||||||
|
languages,
|
||||||
|
segments,
|
||||||
|
posts,
|
||||||
|
render_fun,
|
||||||
|
on_output \\ nil
|
||||||
|
) do
|
||||||
total_items = length(posts)
|
total_items = length(posts)
|
||||||
total_pages = page_count(total_items, plan.max_posts_per_page)
|
total_pages = page_count(total_items, plan.max_posts_per_page)
|
||||||
|
|
||||||
@@ -483,6 +545,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
Enum.map(languages, fn language ->
|
Enum.map(languages, fn language ->
|
||||||
route_language = route_language(plan.language, language)
|
route_language = route_language(plan.language, language)
|
||||||
|
|
||||||
|
output =
|
||||||
{archive_path(route_language, segments, page_number),
|
{archive_path(route_language, segments, page_number),
|
||||||
render_fun.(
|
render_fun.(
|
||||||
page_posts,
|
page_posts,
|
||||||
@@ -496,11 +559,14 @@ defmodule BDS.Generation.Outputs do
|
|||||||
segments
|
segments
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build_single_outputs(String.t(), String.t(), [map()], map(), map()) :: [
|
@spec build_single_outputs(String.t(), String.t(), [map()], map(), map(), output_callback()) ::
|
||||||
|
[
|
||||||
{String.t(), iodata()}
|
{String.t(), iodata()}
|
||||||
]
|
]
|
||||||
def build_single_outputs(
|
def build_single_outputs(
|
||||||
@@ -508,15 +574,18 @@ defmodule BDS.Generation.Outputs do
|
|||||||
main_language,
|
main_language,
|
||||||
published_posts,
|
published_posts,
|
||||||
translations_by_post_language,
|
translations_by_post_language,
|
||||||
localized_posts_by_language
|
localized_posts_by_language,
|
||||||
|
on_output \\ nil
|
||||||
) do
|
) do
|
||||||
post_outputs =
|
post_outputs =
|
||||||
Enum.map(published_posts, fn post ->
|
Enum.map(published_posts, fn post ->
|
||||||
canonical_variant = Map.get(translations_by_post_language, {post.id, main_language}, post)
|
canonical_variant = Map.get(translations_by_post_language, {post.id, main_language}, post)
|
||||||
|
render_language = effective_render_language(canonical_variant.language, main_language)
|
||||||
body = load_body(project_id, canonical_variant.file_path, canonical_variant.content)
|
body = load_body(project_id, canonical_variant.file_path, canonical_variant.content)
|
||||||
|
|
||||||
effective_slug = effective_template_slug(project_id, post)
|
effective_slug = effective_template_slug(project_id, post)
|
||||||
|
|
||||||
|
output =
|
||||||
{post_output_path(post),
|
{post_output_path(post),
|
||||||
render_post_output(
|
render_post_output(
|
||||||
project_id,
|
project_id,
|
||||||
@@ -526,7 +595,7 @@ defmodule BDS.Generation.Outputs do
|
|||||||
title: canonical_variant.title,
|
title: canonical_variant.title,
|
||||||
content: body,
|
content: body,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
language: canonical_variant.language,
|
language: render_language,
|
||||||
excerpt: canonical_variant.excerpt,
|
excerpt: canonical_variant.excerpt,
|
||||||
_post_record: canonical_variant
|
_post_record: canonical_variant
|
||||||
},
|
},
|
||||||
@@ -535,20 +604,24 @@ defmodule BDS.Generation.Outputs do
|
|||||||
canonical_variant.title,
|
canonical_variant.title,
|
||||||
body,
|
body,
|
||||||
post.slug,
|
post.slug,
|
||||||
canonical_variant.language
|
render_language
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
translation_outputs =
|
translation_outputs =
|
||||||
localized_posts_by_language
|
localized_posts_by_language
|
||||||
|> Enum.flat_map(fn {language, posts} ->
|
|> Enum.flat_map(fn {language, posts} ->
|
||||||
Enum.map(posts, fn post ->
|
Enum.map(posts, fn post ->
|
||||||
|
render_language = effective_render_language(post.language, language)
|
||||||
body = load_body(project_id, post.file_path, post.content)
|
body = load_body(project_id, post.file_path, post.content)
|
||||||
|
|
||||||
effective_slug = effective_template_slug(project_id, post)
|
effective_slug = effective_template_slug(project_id, post)
|
||||||
|
|
||||||
|
output =
|
||||||
{post_output_path(post, language),
|
{post_output_path(post, language),
|
||||||
render_post_output(
|
render_post_output(
|
||||||
project_id,
|
project_id,
|
||||||
@@ -558,12 +631,14 @@ defmodule BDS.Generation.Outputs do
|
|||||||
title: post.title,
|
title: post.title,
|
||||||
content: body,
|
content: body,
|
||||||
slug: post.slug,
|
slug: post.slug,
|
||||||
language: post.language,
|
language: render_language,
|
||||||
excerpt: post.excerpt,
|
excerpt: post.excerpt,
|
||||||
_post_record: post
|
_post_record: post
|
||||||
},
|
},
|
||||||
fn -> render_post_page(post.title, body, post.slug, post.language) end
|
fn -> render_post_page(post.title, body, post.slug, render_language) end
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
report_output(output, on_output)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -583,4 +658,23 @@ defmodule BDS.Generation.Outputs do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp effective_render_language(language, _fallback_language)
|
||||||
|
when is_binary(language) and language != "" do
|
||||||
|
language
|
||||||
|
end
|
||||||
|
|
||||||
|
defp effective_render_language(_language, fallback_language)
|
||||||
|
when is_binary(fallback_language) and fallback_language != "" do
|
||||||
|
fallback_language
|
||||||
|
end
|
||||||
|
|
||||||
|
defp effective_render_language(_language, _fallback_language), do: "en"
|
||||||
|
|
||||||
|
defp report_output(output, nil), do: output
|
||||||
|
|
||||||
|
defp report_output(output, on_output) when is_function(on_output, 1) do
|
||||||
|
on_output.(output)
|
||||||
|
output
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -245,7 +245,9 @@ defmodule BDS.Generation.Validation do
|
|||||||
requested_tag_slugs: MapSet.new(),
|
requested_tag_slugs: MapSet.new(),
|
||||||
requested_years: MapSet.new(),
|
requested_years: MapSet.new(),
|
||||||
requested_year_months: MapSet.new(),
|
requested_year_months: MapSet.new(),
|
||||||
|
requested_year_month_days: MapSet.new(),
|
||||||
requested_post_routes: [],
|
requested_post_routes: [],
|
||||||
|
requested_page_slugs: MapSet.new(),
|
||||||
language_plans: %{}
|
language_plans: %{}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -277,27 +279,37 @@ defmodule BDS.Generation.Validation do
|
|||||||
)
|
)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
case Regex.run(~r|^/(\d{4})/(\d{2})(?:/page/\d+)?$|, path) do
|
classify_archive_or_page_validation_path(path, plan)
|
||||||
[_, year, month] ->
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp classify_archive_or_page_validation_path(path, plan) do
|
||||||
|
cond do
|
||||||
|
Regex.match?(~r|^/(\d{4})/(\d{2})/(\d{2})(?:/page/\d+)?$|, path) ->
|
||||||
|
[_, year, month, day] = Regex.run(~r|^/(\d{4})/(\d{2})/(\d{2})(?:/page/\d+)?$|, path)
|
||||||
|
update_in(plan.requested_year_month_days, &MapSet.put(&1, "#{year}/#{month}/#{day}"))
|
||||||
|
|
||||||
|
Regex.match?(~r|^/(\d{4})/(\d{2})(?:/page/\d+)?$|, path) ->
|
||||||
|
[_, year, month] = Regex.run(~r|^/(\d{4})/(\d{2})(?:/page/\d+)?$|, path)
|
||||||
update_in(plan.requested_year_months, &MapSet.put(&1, "#{year}/#{month}"))
|
update_in(plan.requested_year_months, &MapSet.put(&1, "#{year}/#{month}"))
|
||||||
|
|
||||||
nil ->
|
Regex.match?(~r|^/(\d{4})(?:/page/\d+)?$|, path) ->
|
||||||
case Regex.run(~r|^/(\d{4})(?:/page/\d+)?$|, path) do
|
[_, year] = Regex.run(~r|^/(\d{4})(?:/page/\d+)?$|, path)
|
||||||
[_, year] ->
|
|
||||||
update_in(plan.requested_years, &MapSet.put(&1, String.to_integer(year)))
|
update_in(plan.requested_years, &MapSet.put(&1, String.to_integer(year)))
|
||||||
|
|
||||||
nil ->
|
path == "/" or Regex.match?(~r|^/page/\d+$|, path) ->
|
||||||
if path == "/" or Regex.match?(~r|^/page/\d+$|, path) do
|
|
||||||
%{plan | request_root_routes: true}
|
%{plan | request_root_routes: true}
|
||||||
else
|
|
||||||
|
Regex.match?(~r|^/([^/]+)$|, path) ->
|
||||||
|
[_, slug] = Regex.run(~r|^/([^/]+)$|, path)
|
||||||
|
update_in(plan.requested_page_slugs, &MapSet.put(&1, slug))
|
||||||
|
|
||||||
|
true ->
|
||||||
%{plan | requires_fallback_section_render: true}
|
%{plan | requires_fallback_section_render: true}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec build_targeted_validation_plan(map(), [map()]) :: map()
|
@spec build_targeted_validation_plan(map(), [map()]) :: map()
|
||||||
def build_targeted_validation_plan(initial_plan, published_posts) do
|
def build_targeted_validation_plan(initial_plan, published_posts) do
|
||||||
@@ -334,10 +346,14 @@ defmodule BDS.Generation.Validation do
|
|||||||
[:requested_year_months],
|
[:requested_year_months],
|
||||||
&MapSet.put(&1, route_month_key(route.year, route.month))
|
&MapSet.put(&1, route_month_key(route.year, route.month))
|
||||||
)
|
)
|
||||||
|
|> update_in(
|
||||||
|
[:requested_year_month_days],
|
||||||
|
&MapSet.put(&1, route_day_key(route.year, route.month, route.day))
|
||||||
|
)
|
||||||
|> Map.put(:request_root_routes, true)
|
|> Map.put(:request_root_routes, true)
|
||||||
|
|
||||||
post ->
|
post ->
|
||||||
{year, month, _day} = local_date_parts!(post.created_at)
|
{year, month, day} = local_date_parts!(post.created_at)
|
||||||
|
|
||||||
acc
|
acc
|
||||||
|> update_in([:requested_category_slugs], fn set ->
|
|> update_in([:requested_category_slugs], fn set ->
|
||||||
@@ -355,10 +371,15 @@ defmodule BDS.Generation.Validation do
|
|||||||
[:requested_year_months],
|
[:requested_year_months],
|
||||||
&MapSet.put(&1, route_month_key(year, month))
|
&MapSet.put(&1, route_month_key(year, month))
|
||||||
)
|
)
|
||||||
|
|> update_in(
|
||||||
|
[:requested_year_month_days],
|
||||||
|
&MapSet.put(&1, route_day_key(year, month, day))
|
||||||
|
)
|
||||||
|> Map.put(:request_root_routes, true)
|
|> Map.put(:request_root_routes, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|> cascade_requested_date_archives()
|
||||||
|
|
||||||
language_plans =
|
language_plans =
|
||||||
initial_plan.language_plans
|
initial_plan.language_plans
|
||||||
@@ -384,7 +405,8 @@ defmodule BDS.Generation.Validation do
|
|||||||
post.slug == route.slug and year == route.year and month == route.month and day == route.day
|
post.slug == route.slug and year == route.year and month == route.month and day == route.day
|
||||||
end
|
end
|
||||||
|
|
||||||
defp route_key(year, month, day, slug) do
|
@spec route_key(integer(), integer(), integer(), String.t()) :: String.t()
|
||||||
|
def route_key(year, month, day, slug) do
|
||||||
"#{year}/#{String.pad_leading(Integer.to_string(month), 2, "0")}/#{String.pad_leading(Integer.to_string(day), 2, "0")}/#{slug}"
|
"#{year}/#{String.pad_leading(Integer.to_string(month), 2, "0")}/#{String.pad_leading(Integer.to_string(day), 2, "0")}/#{slug}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -392,6 +414,33 @@ defmodule BDS.Generation.Validation do
|
|||||||
"#{year}/#{String.pad_leading(Integer.to_string(month), 2, "0")}"
|
"#{year}/#{String.pad_leading(Integer.to_string(month), 2, "0")}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp route_day_key(year, month, day) do
|
||||||
|
"#{route_month_key(year, month)}/#{String.pad_leading(Integer.to_string(day), 2, "0")}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cascade_requested_date_archives(plan) do
|
||||||
|
plan
|
||||||
|
|> cascade_requested_year_month_days()
|
||||||
|
|> cascade_requested_year_months()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cascade_requested_year_month_days(plan) do
|
||||||
|
Enum.reduce(plan.requested_year_month_days, plan, fn year_month_day, acc ->
|
||||||
|
[year, month, _day] = String.split(year_month_day, "/", parts: 3)
|
||||||
|
|
||||||
|
acc
|
||||||
|
|> update_in([:requested_year_months], &MapSet.put(&1, "#{year}/#{month}"))
|
||||||
|
|> update_in([:requested_years], &MapSet.put(&1, String.to_integer(year)))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cascade_requested_year_months(plan) do
|
||||||
|
Enum.reduce(plan.requested_year_months, plan, fn year_month, acc ->
|
||||||
|
[year | _rest] = String.split(year_month, "/", parts: 2)
|
||||||
|
update_in(acc.requested_years, &MapSet.put(&1, String.to_integer(year)))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp extract_language_path(path, additional_languages) do
|
defp extract_language_path(path, additional_languages) do
|
||||||
case Regex.run(~r|^/([a-z]{2,3})(/.*)?$|, path) do
|
case Regex.run(~r|^/([a-z]{2,3})(/.*)?$|, path) do
|
||||||
[_, language, suffix] ->
|
[_, language, suffix] ->
|
||||||
@@ -413,83 +462,6 @@ defmodule BDS.Generation.Validation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec targeted_output?(String.t(), map(), String.t() | nil, [String.t()]) :: boolean()
|
|
||||||
def targeted_output?(relative_path, targeted_plan, main_language, additional_languages) do
|
|
||||||
{language, stripped_path} =
|
|
||||||
extract_relative_output_language(relative_path, additional_languages)
|
|
||||||
|
|
||||||
plan =
|
|
||||||
case language do
|
|
||||||
nil -> targeted_plan
|
|
||||||
value -> Map.get(targeted_plan.language_plans, value, empty_validation_path_plan())
|
|
||||||
end
|
|
||||||
|
|
||||||
targeted_output_for_plan?(stripped_path, plan, main_language == language or is_nil(language))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extract_relative_output_language(relative_path, additional_languages) do
|
|
||||||
segments = String.split(relative_path, "/", trim: true)
|
|
||||||
|
|
||||||
case segments do
|
|
||||||
[language | rest] ->
|
|
||||||
if language in additional_languages do
|
|
||||||
{language, Path.join(rest)}
|
|
||||||
else
|
|
||||||
{nil, relative_path}
|
|
||||||
end
|
|
||||||
|
|
||||||
_other ->
|
|
||||||
{nil, relative_path}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp targeted_output_for_plan?(
|
|
||||||
_relative_path,
|
|
||||||
%{requires_fallback_section_render: true},
|
|
||||||
_main?
|
|
||||||
),
|
|
||||||
do: true
|
|
||||||
|
|
||||||
defp targeted_output_for_plan?(relative_path, plan, _main?) do
|
|
||||||
cond do
|
|
||||||
relative_path in ["index.html", "404.html", "feed.xml", "atom.xml"] ->
|
|
||||||
plan.request_root_routes
|
|
||||||
|
|
||||||
Regex.match?(~r|^category/([^/]+)(?:/page/\d+)?/index\.html$|, relative_path) ->
|
|
||||||
[_, slug] = Regex.run(~r|^category/([^/]+)(?:/page/\d+)?/index\.html$|, relative_path)
|
|
||||||
MapSet.member?(plan.requested_category_slugs, slug)
|
|
||||||
|
|
||||||
Regex.match?(~r|^tag/([^/]+)/index\.html$|, relative_path) ->
|
|
||||||
[_, slug] = Regex.run(~r|^tag/([^/]+)/index\.html$|, relative_path)
|
|
||||||
MapSet.member?(plan.requested_tag_slugs, slug)
|
|
||||||
|
|
||||||
Regex.match?(~r|^(\d{4})/(\d{2})/(\d{2})/([^/]+)/index\.html$|, relative_path) ->
|
|
||||||
[_, year, month, day, slug] =
|
|
||||||
Regex.run(~r|^(\d{4})/(\d{2})/(\d{2})/([^/]+)/index\.html$|, relative_path)
|
|
||||||
|
|
||||||
MapSet.member?(
|
|
||||||
plan.requested_post_routes,
|
|
||||||
route_key(
|
|
||||||
String.to_integer(year),
|
|
||||||
String.to_integer(month),
|
|
||||||
String.to_integer(day),
|
|
||||||
slug
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Regex.match?(~r|^(\d{4})/(\d{2})/index\.html$|, relative_path) ->
|
|
||||||
[_, year, month] = Regex.run(~r|^(\d{4})/(\d{2})/index\.html$|, relative_path)
|
|
||||||
MapSet.member?(plan.requested_year_months, "#{year}/#{month}")
|
|
||||||
|
|
||||||
Regex.match?(~r|^(\d{4})/index\.html$|, relative_path) ->
|
|
||||||
[_, year] = Regex.run(~r|^(\d{4})/index\.html$|, relative_path)
|
|
||||||
MapSet.member?(plan.requested_years, String.to_integer(year))
|
|
||||||
|
|
||||||
true ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec route_html_path?(String.t()) :: boolean()
|
@spec route_html_path?(String.t()) :: boolean()
|
||||||
def route_html_path?(relative_path), do: String.ends_with?(relative_path, "index.html")
|
def route_html_path?(relative_path), do: String.ends_with?(relative_path, "index.html")
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ defmodule BDS.Rendering.PostRendering do
|
|||||||
metadata = RenderMetadata.project_metadata(project_id)
|
metadata = RenderMetadata.project_metadata(project_id)
|
||||||
template_context = TemplateSelection.template_render_context(project_id)
|
template_context = TemplateSelection.template_render_context(project_id)
|
||||||
|
|
||||||
language = MapUtils.attr(assigns, :language, metadata.main_language || "en")
|
language = MapUtils.attr(assigns, :language) || metadata.main_language || "en"
|
||||||
|
|
||||||
main_language = metadata.main_language || language
|
main_language = metadata.main_language || language
|
||||||
post_record = Map.get(assigns, :_post_record) || load_post_record(assigns)
|
post_record = Map.get(assigns, :_post_record) || load_post_record(assigns)
|
||||||
@@ -58,7 +58,8 @@ defmodule BDS.Rendering.PostRendering do
|
|||||||
|
|
||||||
%{
|
%{
|
||||||
language: language,
|
language: language,
|
||||||
language_prefix: assign_override(
|
language_prefix:
|
||||||
|
assign_override(
|
||||||
assigns,
|
assigns,
|
||||||
:language_prefix,
|
:language_prefix,
|
||||||
"language_prefix",
|
"language_prefix",
|
||||||
@@ -70,7 +71,8 @@ defmodule BDS.Rendering.PostRendering do
|
|||||||
:page_title,
|
:page_title,
|
||||||
MapUtils.attr(assigns, :title)
|
MapUtils.attr(assigns, :title)
|
||||||
),
|
),
|
||||||
pico_stylesheet_href: assign_override(
|
pico_stylesheet_href:
|
||||||
|
assign_override(
|
||||||
assigns,
|
assigns,
|
||||||
:pico_stylesheet_href,
|
:pico_stylesheet_href,
|
||||||
"pico_stylesheet_href",
|
"pico_stylesheet_href",
|
||||||
|
|||||||
@@ -221,6 +221,86 @@ msgstr "Auswahl anwenden"
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr "Theme anwenden"
|
msgstr "Theme anwenden"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr "Website-Validierung anwenden"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr "Aufgaben zum Anwenden der Website-Validierung eingereiht"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr "Suchindex aufbauen"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr "Suchindex aufgebaut"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr "Überzählige URLs werden gelöscht..."
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr "Keine überzähligen URLs zu löschen"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr "Schritte zum Anwenden der Validierung werden geplant..."
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr "Anwenden der Validierung vorbereiten"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr "Kalender neu erzeugt"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr "Kategoriearchive rendern"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr "Kategoriearchive gerendert"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr "Datumsarchive rendern"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr "Datumsarchive gerendert"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr "Einzelbeiträge rendern"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr "Einzelbeiträge gerendert"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr "Website-Kern rendern"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr "Website-Kern gerendert"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr "Tag-Archive rendern"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr "Tag-Archive gerendert"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr "Anwenden der Validierung vorbereitet"
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|||||||
@@ -221,6 +221,86 @@ msgstr ""
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|||||||
@@ -221,6 +221,86 @@ msgstr "Aplicar seleccionados"
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr "Aplicar tema"
|
msgstr "Aplicar tema"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr "Aplicar validación del sitio"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr "Tareas para aplicar la validación del sitio en cola"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr "Crear índice de búsqueda"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr "Índice de búsqueda creado"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr "Eliminando URLs sobrantes..."
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr "No hay URLs sobrantes para eliminar"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr "Planificando pasos para aplicar la validación..."
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr "Preparar aplicación de validación"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr "Calendario regenerado"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr "Renderizar archivos de categorías"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr "Archivos de categorías renderizados"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr "Renderizar archivos por fecha"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr "Archivos por fecha renderizados"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr "Renderizar entradas individuales"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr "Entradas individuales renderizadas"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr "Renderizar núcleo del sitio"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr "Núcleo del sitio renderizado"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr "Renderizar archivos de etiquetas"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr "Archivos de etiquetas renderizados"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr "Aplicación de validación preparada"
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|||||||
@@ -221,6 +221,86 @@ msgstr "Appliquer la selection"
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr "Appliquer le thème"
|
msgstr "Appliquer le thème"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr "Appliquer la validation du site"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr "Tâches d’application de la validation du site mises en file"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr "Construire l’index de recherche"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr "Index de recherche construit"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr "Suppression des URLs en trop..."
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr "Aucune URL en trop à supprimer"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr "Planification de l’application de la validation..."
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr "Préparer l’application de la validation"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr "Calendrier régénéré"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr "Rendre les archives de catégories"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr "Archives de catégories rendues"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr "Rendre les archives par date"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr "Archives par date rendues"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr "Rendre les articles individuels"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr "Articles individuels rendus"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr "Rendre le cœur du site"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr "Cœur du site rendu"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr "Rendre les archives de tags"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr "Archives de tags rendues"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr "Application de la validation préparée"
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|||||||
@@ -221,6 +221,86 @@ msgstr "Applica selezionati"
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr "Applica tema"
|
msgstr "Applica tema"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr "Applica validazione sito"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr "Attività per applicare la validazione sito in coda"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr "Crea indice di ricerca"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr "Indice di ricerca creato"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr "Eliminazione URL extra..."
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr "Nessun URL extra da eliminare"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr "Pianificazione dei passaggi di applicazione della validazione..."
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr "Prepara applicazione validazione"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr "Calendario rigenerato"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr "Renderizza archivi categorie"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr "Archivi categorie renderizzati"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr "Renderizza archivi data"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr "Archivi data renderizzati"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr "Renderizza post singoli"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr "Post singoli renderizzati"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr "Renderizza nucleo sito"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr "Nucleo sito renderizzato"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr "Renderizza archivi tag"
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr "Archivi tag renderizzati"
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr "Applicazione della validazione preparata"
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:333
|
#: lib/bds/desktop/shell_live/misc_editor.ex:319
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{canonical} = %{translation}"
|
msgid "%{canonical} = %{translation}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -92,7 +92,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:42
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:42
|
||||||
#: lib/bds/desktop/shell_live/overlay_manager.ex:72
|
#: lib/bds/desktop/shell_live/overlay_manager.ex:72
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:888
|
#: lib/bds/desktop/shell_live/post_editor.ex:918
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:43
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:43
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "AI Suggestions"
|
msgid "AI Suggestions"
|
||||||
@@ -234,6 +234,86 @@ msgstr ""
|
|||||||
msgid "Apply Theme"
|
msgid "Apply Theme"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Apply Site Validation tasks queued"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Build Search Index complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Deleting extra URLs..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "No extra URLs to delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Planning validation apply steps..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Prepare Validation Apply"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Regenerate Calendar complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Category Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Date Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Single Posts complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Site Core complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_commands.ex
|
||||||
|
msgid "Render Tag Archives complete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/generation.ex
|
||||||
|
msgid "Validation apply prepared"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:182
|
#: lib/bds/desktop/shell_data.ex:182
|
||||||
#: lib/bds/ui/sidebar.ex:331
|
#: lib/bds/ui/sidebar.ex:331
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -272,12 +352,12 @@ msgstr ""
|
|||||||
#: lib/bds/desktop/shell_data.ex:98
|
#: lib/bds/desktop/shell_data.ex:98
|
||||||
#: lib/bds/desktop/shell_live.ex:422
|
#: lib/bds/desktop/shell_live.ex:422
|
||||||
#: lib/bds/desktop/shell_live/chat_editor.ex:234
|
#: lib/bds/desktop/shell_live/chat_editor.ex:234
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:160
|
#: lib/bds/desktop/shell_live/media_editor.ex:171
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:353
|
#: lib/bds/desktop/shell_live/media_editor.ex:364
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:546
|
#: lib/bds/desktop/shell_live/media_editor.ex:557
|
||||||
#: lib/bds/desktop/shell_live/overlay_manager.ex:73
|
#: lib/bds/desktop/shell_live/overlay_manager.ex:73
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:755
|
#: lib/bds/desktop/shell_live/post_editor.ex:763
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:804
|
#: lib/bds/desktop/shell_live/post_editor.ex:812
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Automatic AI actions stay gated by airplane mode."
|
msgid "Automatic AI actions stay gated by airplane mode."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -380,8 +460,8 @@ msgstr ""
|
|||||||
#: lib/bds/desktop/shell_live/import_editor.ex:1024
|
#: lib/bds/desktop/shell_live/import_editor.ex:1024
|
||||||
#: lib/bds/desktop/shell_live/import_editor.ex:1188
|
#: lib/bds/desktop/shell_live/import_editor.ex:1188
|
||||||
#: lib/bds/desktop/shell_live/index.html.heex:336
|
#: lib/bds/desktop/shell_live/index.html.heex:336
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:755
|
#: lib/bds/desktop/shell_live/misc_editor.ex:741
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:756
|
#: lib/bds/desktop/shell_live/misc_editor.ex:742
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:256
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:256
|
||||||
#: lib/bds/desktop/shell_live/settings_editor/managed_categories.ex:59
|
#: lib/bds/desktop/shell_live/settings_editor/managed_categories.ex:59
|
||||||
#: lib/bds/desktop/shell_live/settings_editor/managed_categories.ex:75
|
#: lib/bds/desktop/shell_live/settings_editor/managed_categories.ex:75
|
||||||
@@ -439,7 +519,7 @@ msgstr ""
|
|||||||
msgid "Check Syntax"
|
msgid "Check Syntax"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:486
|
#: lib/bds/desktop/shell_live/misc_editor.ex:472
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Checked DB rows: %{dbRows} · Checked files: %{files} · Invalid DB rows: %{invalidDb} · Invalid files: %{invalidFiles}"
|
msgid "Checked DB rows: %{dbRows} · Checked files: %{files} · Invalid DB rows: %{invalidDb} · Invalid files: %{invalidFiles}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -675,7 +755,7 @@ msgstr ""
|
|||||||
msgid "Delete Media"
|
msgid "Delete Media"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:396
|
#: lib/bds/desktop/shell_live/media_editor.ex:407
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Delete Translation"
|
msgid "Delete Translation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -691,7 +771,7 @@ msgstr ""
|
|||||||
msgid "Delete this unpublished draft"
|
msgid "Delete this unpublished draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:120
|
#: lib/bds/desktop/shell_live/misc_editor.ex:106
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Deleted %{dbRows} DB rows and %{files} files, flushed %{flushed} translations to disk"
|
msgid "Deleted %{dbRows} DB rows and %{files} files, flushed %{flushed} translations to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -716,14 +796,14 @@ msgstr ""
|
|||||||
msgid "Detect"
|
msgid "Detect"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:159
|
#: lib/bds/desktop/shell_live/media_editor.ex:170
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:198
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:203
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:209
|
#: lib/bds/desktop/shell_live/media_editor.ex:209
|
||||||
|
#: lib/bds/desktop/shell_live/media_editor.ex:214
|
||||||
|
#: lib/bds/desktop/shell_live/media_editor.ex:220
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:59
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:59
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:754
|
#: lib/bds/desktop/shell_live/post_editor.ex:762
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:783
|
#: lib/bds/desktop/shell_live/post_editor.ex:791
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:789
|
#: lib/bds/desktop/shell_live/post_editor.ex:797
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Detect Language"
|
msgid "Detect Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -854,8 +934,8 @@ msgstr ""
|
|||||||
msgid "Editor Settings"
|
msgid "Editor Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:757
|
#: lib/bds/desktop/shell_live/misc_editor.ex:743
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:780
|
#: lib/bds/desktop/shell_live/misc_editor.ex:766
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Embeddings"
|
msgid "Embeddings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -980,11 +1060,11 @@ msgstr ""
|
|||||||
msgid "Find Duplicate Posts"
|
msgid "Find Duplicate Posts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:176
|
#: lib/bds/desktop/shell_live/misc_editor.ex:162
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:182
|
#: lib/bds/desktop/shell_live/misc_editor.ex:168
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:209
|
#: lib/bds/desktop/shell_live/misc_editor.ex:195
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:216
|
#: lib/bds/desktop/shell_live/misc_editor.ex:202
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:503
|
#: lib/bds/desktop/shell_live/misc_editor.ex:489
|
||||||
#: lib/bds/ui/registry.ex:139
|
#: lib/bds/ui/registry.ex:139
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Find Duplicates"
|
msgid "Find Duplicates"
|
||||||
@@ -1013,7 +1093,7 @@ msgstr ""
|
|||||||
msgid "Git"
|
msgid "Git"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:560
|
#: lib/bds/desktop/shell_live/misc_editor.ex:546
|
||||||
#: lib/bds/ui/registry.ex:113
|
#: lib/bds/ui/registry.ex:113
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Git Diff"
|
msgid "Git Diff"
|
||||||
@@ -1048,8 +1128,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:116
|
#: lib/bds/desktop/shell_data.ex:116
|
||||||
#: lib/bds/desktop/shell_live/index.html.heex:666
|
#: lib/bds/desktop/shell_live/index.html.heex:666
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:711
|
#: lib/bds/desktop/shell_live/media_editor.ex:731
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1006
|
#: lib/bds/desktop/shell_live/post_editor.ex:1036
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Idle"
|
msgid "Idle"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1219,8 +1299,8 @@ msgstr ""
|
|||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:210
|
#: lib/bds/desktop/shell_live/media_editor.ex:221
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:790
|
#: lib/bds/desktop/shell_live/post_editor.ex:798
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Language detection failed."
|
msgid "Language detection failed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1230,7 +1310,7 @@ msgstr ""
|
|||||||
msgid "Light"
|
msgid "Light"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:256
|
#: lib/bds/desktop/shell_live/media_editor.ex:267
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:215
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:215
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Link to Post"
|
msgid "Link to Post"
|
||||||
@@ -1304,7 +1384,7 @@ msgstr ""
|
|||||||
msgid "Mapped"
|
msgid "Mapped"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1009
|
#: lib/bds/desktop/shell_live/post_editor.ex:1039
|
||||||
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:123
|
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:123
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Markdown"
|
msgid "Markdown"
|
||||||
@@ -1316,10 +1396,10 @@ msgid "Max Posts Per Page"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/menu_bar.ex:168
|
#: lib/bds/desktop/menu_bar.ex:168
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:498
|
#: lib/bds/desktop/shell_live/media_editor.ex:509
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:502
|
#: lib/bds/desktop/shell_live/media_editor.ex:513
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:749
|
#: lib/bds/desktop/shell_live/misc_editor.ex:735
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:776
|
#: lib/bds/desktop/shell_live/misc_editor.ex:762
|
||||||
#: lib/bds/desktop/shell_live/sidebar_components.ex:654
|
#: lib/bds/desktop/shell_live/sidebar_components.ex:654
|
||||||
#: lib/bds/desktop/shell_live/sidebar_delete.ex:175
|
#: lib/bds/desktop/shell_live/sidebar_delete.ex:175
|
||||||
#: lib/bds/ui/registry.ex:30
|
#: lib/bds/ui/registry.ex:30
|
||||||
@@ -1334,7 +1414,7 @@ msgstr ""
|
|||||||
msgid "Media (%{count})"
|
msgid "Media (%{count})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:498
|
#: lib/bds/desktop/shell_live/media_editor.ex:509
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Media saved"
|
msgid "Media saved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1361,9 +1441,9 @@ msgid "Metadata"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/menu_bar.ex:185
|
#: lib/bds/desktop/menu_bar.ex:185
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:228
|
#: lib/bds/desktop/shell_live/misc_editor.ex:214
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:240
|
#: lib/bds/desktop/shell_live/misc_editor.ex:226
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:462
|
#: lib/bds/desktop/shell_live/misc_editor.ex:448
|
||||||
#: lib/bds/ui/registry.ex:111
|
#: lib/bds/ui/registry.ex:111
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Metadata Diff"
|
msgid "Metadata Diff"
|
||||||
@@ -1465,7 +1545,7 @@ msgstr ""
|
|||||||
msgid "No git history yet"
|
msgid "No git history yet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:473
|
#: lib/bds/desktop/shell_live/misc_editor.ex:459
|
||||||
#: lib/bds/desktop/shell_live/overlay_html/shell_overlay.html.heex:72
|
#: lib/bds/desktop/shell_live/overlay_html/shell_overlay.html.heex:72
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:321
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:321
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:333
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:333
|
||||||
@@ -1505,12 +1585,12 @@ msgstr ""
|
|||||||
msgid "No media files"
|
msgid "No media files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:592
|
#: lib/bds/desktop/shell_live/misc_editor.ex:578
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "No metadata diff items selected"
|
msgid "No metadata diff items selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:621
|
#: lib/bds/desktop/shell_live/misc_editor.ex:607
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "No orphan files selected"
|
msgid "No orphan files selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1535,7 +1615,7 @@ msgstr ""
|
|||||||
msgid "No posts yet"
|
msgid "No posts yet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:589
|
#: lib/bds/desktop/shell_live/misc_editor.ex:575
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "No repair action available"
|
msgid "No repair action available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1560,7 +1640,7 @@ msgstr ""
|
|||||||
msgid "No translations"
|
msgid "No translations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:567
|
#: lib/bds/desktop/shell_live/misc_editor.ex:553
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "No unstaged changes"
|
msgid "No unstaged changes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1775,7 +1855,7 @@ msgstr ""
|
|||||||
msgid "Pages (%{count})"
|
msgid "Pages (%{count})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:176
|
#: lib/bds/desktop/shell_live/misc_editor.ex:162
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Pair dismissed"
|
msgid "Pair dismissed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1801,17 +1881,17 @@ msgstr ""
|
|||||||
msgid "Persist the detected language for this media item"
|
msgid "Persist the detected language for this media item"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:747
|
#: lib/bds/desktop/shell_live/misc_editor.ex:733
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:529
|
#: lib/bds/desktop/shell_live/post_editor.ex:537
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:533
|
#: lib/bds/desktop/shell_live/post_editor.ex:541
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:572
|
#: lib/bds/desktop/shell_live/post_editor.ex:580
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:576
|
#: lib/bds/desktop/shell_live/post_editor.ex:584
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:614
|
#: lib/bds/desktop/shell_live/post_editor.ex:622
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:629
|
#: lib/bds/desktop/shell_live/post_editor.ex:637
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:658
|
#: lib/bds/desktop/shell_live/post_editor.ex:666
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:661
|
#: lib/bds/desktop/shell_live/post_editor.ex:669
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:741
|
#: lib/bds/desktop/shell_live/post_editor.ex:749
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:744
|
#: lib/bds/desktop/shell_live/post_editor.ex:752
|
||||||
#: lib/bds/desktop/shell_live/sidebar_components.ex:651
|
#: lib/bds/desktop/shell_live/sidebar_components.ex:651
|
||||||
#: lib/bds/desktop/shell_live/sidebar_delete.ex:174
|
#: lib/bds/desktop/shell_live/sidebar_delete.ex:174
|
||||||
#: lib/bds/ui/registry.ex:99
|
#: lib/bds/ui/registry.ex:99
|
||||||
@@ -1836,24 +1916,24 @@ msgstr ""
|
|||||||
msgid "Post Template"
|
msgid "Post Template"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:315
|
#: lib/bds/desktop/shell_live/misc_editor.ex:301
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Post is marked as do-not-translate but has translations"
|
msgid "Post is marked as do-not-translate but has translations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:572
|
#: lib/bds/desktop/shell_live/post_editor.ex:580
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Post published"
|
msgid "Post published"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:529
|
#: lib/bds/desktop/shell_live/post_editor.ex:537
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Post saved"
|
msgid "Post saved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/menu_bar.ex:167
|
#: lib/bds/desktop/menu_bar.ex:167
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:678
|
#: lib/bds/desktop/shell_live/misc_editor.ex:664
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:775
|
#: lib/bds/desktop/shell_live/misc_editor.ex:761
|
||||||
#: lib/bds/ui/registry.ex:14
|
#: lib/bds/ui/registry.ex:14
|
||||||
#: lib/bds/ui/sidebar.ex:271
|
#: lib/bds/ui/sidebar.ex:271
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -1870,7 +1950,7 @@ msgstr ""
|
|||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1010
|
#: lib/bds/desktop/shell_live/post_editor.ex:1040
|
||||||
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:124
|
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:124
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Preview"
|
msgid "Preview"
|
||||||
@@ -1892,8 +1972,8 @@ msgid "Preview unavailable"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/index.html.heex:509
|
#: lib/bds/desktop/shell_live/index.html.heex:509
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:753
|
#: lib/bds/desktop/shell_live/misc_editor.ex:739
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:779
|
#: lib/bds/desktop/shell_live/misc_editor.ex:765
|
||||||
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:27
|
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:27
|
||||||
#: lib/bds/ui/sidebar.ex:762
|
#: lib/bds/ui/sidebar.ex:762
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -1939,19 +2019,19 @@ msgid "Publish Selected"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_data.ex:181
|
#: lib/bds/desktop/shell_data.ex:181
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1004
|
#: lib/bds/desktop/shell_live/post_editor.ex:1034
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:472
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:472
|
||||||
#: lib/bds/ui/sidebar.ex:324
|
#: lib/bds/ui/sidebar.ex:324
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Published"
|
msgid "Published"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:318
|
#: lib/bds/desktop/shell_live/misc_editor.ex:304
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Published translation has content stuck in DB instead of filesystem"
|
msgid "Published translation has content stuck in DB instead of filesystem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:754
|
#: lib/bds/desktop/shell_live/misc_editor.ex:740
|
||||||
#: lib/bds/desktop/shell_live/settings_editor/publishing_settings.ex:40
|
#: lib/bds/desktop/shell_live/settings_editor/publishing_settings.ex:40
|
||||||
#: lib/bds/desktop/shell_live/settings_editor/publishing_settings.ex:57
|
#: lib/bds/desktop/shell_live/settings_editor/publishing_settings.ex:57
|
||||||
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:338
|
#: lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex:338
|
||||||
@@ -2038,8 +2118,8 @@ msgstr ""
|
|||||||
msgid "Refresh Online Models"
|
msgid "Refresh Online Models"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:368
|
#: lib/bds/desktop/shell_live/media_editor.ex:379
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:377
|
#: lib/bds/desktop/shell_live/media_editor.ex:388
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Refresh Translation"
|
msgid "Refresh Translation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2095,14 +2175,14 @@ msgstr ""
|
|||||||
msgid "Replace"
|
msgid "Replace"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:142
|
#: lib/bds/desktop/shell_live/media_editor.ex:153
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:150
|
#: lib/bds/desktop/shell_live/media_editor.ex:161
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:86
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:86
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Replace File"
|
msgid "Replace File"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:116
|
#: lib/bds/desktop/shell_live/media_editor.ex:127
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Replace Media File"
|
msgid "Replace Media File"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2137,7 +2217,7 @@ msgstr ""
|
|||||||
msgid "Result"
|
msgid "Result"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1005
|
#: lib/bds/desktop/shell_live/post_editor.ex:1035
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Reverted"
|
msgid "Reverted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2183,13 +2263,13 @@ msgstr ""
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:328
|
#: lib/bds/desktop/shell_live/media_editor.ex:339
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Save Translation"
|
msgid "Save Translation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:710
|
#: lib/bds/desktop/shell_live/media_editor.ex:730
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1003
|
#: lib/bds/desktop/shell_live/post_editor.ex:1033
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Saved"
|
msgid "Saved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2199,7 +2279,7 @@ msgstr ""
|
|||||||
msgid "Scanning entries..."
|
msgid "Scanning entries..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:751
|
#: lib/bds/desktop/shell_live/misc_editor.ex:737
|
||||||
#: lib/bds/desktop/shell_live/sidebar_components.ex:657
|
#: lib/bds/desktop/shell_live/sidebar_components.ex:657
|
||||||
#: lib/bds/desktop/shell_live/sidebar_delete.ex:176
|
#: lib/bds/desktop/shell_live/sidebar_delete.ex:176
|
||||||
#: lib/bds/ui/registry.ex:133
|
#: lib/bds/ui/registry.ex:133
|
||||||
@@ -2227,7 +2307,7 @@ msgstr ""
|
|||||||
msgid "Scripting capabilities are configured at the application layer in the rewrite and do not expose runtime switching here."
|
msgid "Scripting capabilities are configured at the application layer in the rewrite and do not expose runtime switching here."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:777
|
#: lib/bds/desktop/shell_live/misc_editor.ex:763
|
||||||
#: lib/bds/desktop/shell_live/script_editor.ex:119
|
#: lib/bds/desktop/shell_live/script_editor.ex:119
|
||||||
#: lib/bds/desktop/shell_live/script_editor.ex:124
|
#: lib/bds/desktop/shell_live/script_editor.ex:124
|
||||||
#: lib/bds/desktop/shell_live/script_editor.ex:130
|
#: lib/bds/desktop/shell_live/script_editor.ex:130
|
||||||
@@ -2332,7 +2412,7 @@ msgstr ""
|
|||||||
msgid "Select an existing category or press Enter to create a new archive entry"
|
msgid "Select an existing category or press Enter to create a new archive entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:210
|
#: lib/bds/desktop/shell_live/misc_editor.ex:196
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Selected pairs dismissed"
|
msgid "Selected pairs dismissed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -2366,9 +2446,7 @@ msgstr ""
|
|||||||
msgid "Site"
|
msgid "Site"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:95
|
#: lib/bds/desktop/shell_live/misc_editor.ex:412
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:105
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:426
|
|
||||||
#: lib/bds/ui/registry.ex:125
|
#: lib/bds/ui/registry.ex:125
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Site Validation"
|
msgid "Site Validation"
|
||||||
@@ -2510,7 +2588,7 @@ msgstr ""
|
|||||||
msgid "Technology"
|
msgid "Technology"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:752
|
#: lib/bds/desktop/shell_live/misc_editor.ex:738
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:299
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:299
|
||||||
#: lib/bds/desktop/shell_live/sidebar_components.ex:660
|
#: lib/bds/desktop/shell_live/sidebar_components.ex:660
|
||||||
#: lib/bds/desktop/shell_live/sidebar_delete.ex:179
|
#: lib/bds/desktop/shell_live/sidebar_delete.ex:179
|
||||||
@@ -2534,7 +2612,7 @@ msgstr ""
|
|||||||
msgid "Template syntax is valid"
|
msgid "Template syntax is valid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:778
|
#: lib/bds/desktop/shell_live/misc_editor.ex:764
|
||||||
#: lib/bds/desktop/shell_live/template_editor.ex:111
|
#: lib/bds/desktop/shell_live/template_editor.ex:111
|
||||||
#: lib/bds/desktop/shell_live/template_editor.ex:116
|
#: lib/bds/desktop/shell_live/template_editor.ex:116
|
||||||
#: lib/bds/desktop/shell_live/template_editor.ex:121
|
#: lib/bds/desktop/shell_live/template_editor.ex:121
|
||||||
@@ -2658,39 +2736,35 @@ msgstr ""
|
|||||||
msgid "Toggle sidebar"
|
msgid "Toggle sidebar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:352
|
#: lib/bds/desktop/shell_live/media_editor.ex:363
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:545
|
#: lib/bds/desktop/shell_live/media_editor.ex:556
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:566
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:571
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:76
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:76
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:803
|
#: lib/bds/desktop/shell_live/post_editor.ex:811
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:832
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:837
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:60
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:60
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Translate"
|
msgid "Translate"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/bds/desktop/shell_live/misc_editor.ex:105
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:119
|
#: lib/bds/desktop/shell_live/misc_editor.ex:119
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:133
|
#: lib/bds/desktop/shell_live/misc_editor.ex:468
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:482
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Translation Validation"
|
msgid "Translation Validation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:312
|
#: lib/bds/desktop/shell_live/misc_editor.ex:298
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Translation language matches canonical post language"
|
msgid "Translation language matches canonical post language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:321
|
#: lib/bds/desktop/shell_live/misc_editor.ex:307
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Translation points to a missing source post"
|
msgid "Translation points to a missing source post"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:183
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:183
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:748
|
#: lib/bds/desktop/shell_live/misc_editor.ex:734
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:750
|
#: lib/bds/desktop/shell_live/misc_editor.ex:736
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:129
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:129
|
||||||
#: lib/bds/ui/registry.ex:131
|
#: lib/bds/ui/registry.ex:131
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -2735,14 +2809,14 @@ msgstr ""
|
|||||||
msgid "Unknown"
|
msgid "Unknown"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:269
|
#: lib/bds/desktop/shell_live/media_editor.ex:280
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unlink from Post"
|
msgid "Unlink from Post"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/media_editor.ex:709
|
#: lib/bds/desktop/shell_live/media_editor.ex:729
|
||||||
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:10
|
#: lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex:10
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:1002
|
#: lib/bds/desktop/shell_live/post_editor.ex:1032
|
||||||
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:7
|
#: lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex:7
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unsaved"
|
msgid "Unsaved"
|
||||||
@@ -2811,11 +2885,6 @@ msgstr ""
|
|||||||
msgid "Validate Translations"
|
msgid "Validate Translations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/misc_editor.ex:96
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Validation changes applied"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/bds/desktop/menu_bar.ex:146
|
#: lib/bds/desktop/menu_bar.ex:146
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "View"
|
msgid "View"
|
||||||
@@ -3243,12 +3312,12 @@ msgstr ""
|
|||||||
msgid "Move this post to the archive"
|
msgid "Move this post to the archive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:658
|
#: lib/bds/desktop/shell_live/post_editor.ex:666
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Post archived"
|
msgid "Post archived"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:741
|
#: lib/bds/desktop/shell_live/post_editor.ex:749
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Post unarchived"
|
msgid "Post unarchived"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -3427,18 +3496,18 @@ msgstr ""
|
|||||||
msgid "Open a project before importing a blogmark."
|
msgid "Open a project before importing a blogmark."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:684
|
#: lib/bds/desktop/shell_live/post_editor.ex:692
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Added %{name}"
|
msgid "Added %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:691
|
#: lib/bds/desktop/shell_live/post_editor.ex:699
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Failed to import %{path}: %{reason}"
|
msgid "Failed to import %{path}: %{reason}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:683
|
#: lib/bds/desktop/shell_live/post_editor.ex:691
|
||||||
#: lib/bds/desktop/shell_live/post_editor.ex:690
|
#: lib/bds/desktop/shell_live/post_editor.ex:698
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Insert Image"
|
msgid "Insert Image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -458,10 +458,11 @@ rule SiteValidationScan {
|
|||||||
rule SiteValidationApply {
|
rule SiteValidationApply {
|
||||||
when: SiteValidationApplyRequested(report)
|
when: SiteValidationApplyRequested(report)
|
||||||
-- Classifies affected paths into generation sections (core, single, category, tag, date)
|
-- Classifies affected paths into generation sections (core, single, category, tag, date)
|
||||||
-- Renders only affected sections in parallel
|
-- Renders only affected routes (a post's own single page plus only its own
|
||||||
|
-- categories, tags, and date archives); unaffected routes are left untouched
|
||||||
-- Deletes extra HTML files, removes empty directories
|
-- Deletes extra HTML files, removes empty directories
|
||||||
-- Regenerates calendar if anything changed
|
-- Regenerates calendar if anything changed (feed/atom/404/sitemap/search
|
||||||
-- Rebuilds search index if anything rendered or deleted
|
-- index are NOT rebuilt on apply)
|
||||||
ensures: ApplyValidationRequested(report.project_id, report.affected_sections)
|
ensures: ApplyValidationRequested(report.project_id, report.affected_sections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -368,6 +368,76 @@ defmodule BDS.Desktop.ShellCommandsTest do
|
|||||||
assert Map.has_key?(completed.result.payload.summary, :updated_count)
|
assert Map.has_key?(completed.result.payload.summary, :updated_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "apply_site_validation queues visible apply subtasks and revalidates the site", %{
|
||||||
|
project: project,
|
||||||
|
temp_dir: temp_dir
|
||||||
|
} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
Metadata.update_project_metadata(project.id, %{
|
||||||
|
public_url: "https://example.com/blog",
|
||||||
|
main_language: "en",
|
||||||
|
blog_languages: ["en"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Validation Apply Task Post",
|
||||||
|
content: "Validation apply task body",
|
||||||
|
language: "en",
|
||||||
|
categories: ["notes"],
|
||||||
|
tags: ["validation-task"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_post} = Posts.publish_post(post.id)
|
||||||
|
assert {:ok, _generation} = BDS.Generation.generate_site(project.id, [:core, :single])
|
||||||
|
|
||||||
|
post_path = BDS.Generation.post_output_path(published_post)
|
||||||
|
File.rm!(Path.join([temp_dir, "html", post_path]))
|
||||||
|
|
||||||
|
extra_path = Path.join([temp_dir, "html", "obsolete", "index.html"])
|
||||||
|
File.mkdir_p!(Path.dirname(extra_path))
|
||||||
|
File.write!(extra_path, "<html>obsolete</html>")
|
||||||
|
|
||||||
|
post_url_path = BDS.Generation.Paths.relative_path_to_url_path(post_path)
|
||||||
|
|
||||||
|
assert {:ok, report} =
|
||||||
|
BDS.Generation.validate_site(project.id, [:core, :single, :category, :tag, :date])
|
||||||
|
|
||||||
|
assert post_url_path in report.missing_url_paths
|
||||||
|
assert "/obsolete" in report.extra_url_paths
|
||||||
|
|
||||||
|
assert {:ok, result} = ShellCommands.execute("apply_site_validation", %{report: report})
|
||||||
|
|
||||||
|
assert result.kind == "task_queued"
|
||||||
|
assert result.action == "apply_site_validation"
|
||||||
|
assert result.title == "Apply Site Validation"
|
||||||
|
assert is_binary(result.task_id)
|
||||||
|
assert is_binary(result.task_group_id)
|
||||||
|
|
||||||
|
tasks =
|
||||||
|
wait_for_tasks_by_name(
|
||||||
|
[
|
||||||
|
"Prepare Validation Apply",
|
||||||
|
"Render Site Core",
|
||||||
|
"Render Single Posts",
|
||||||
|
"Validate Site"
|
||||||
|
],
|
||||||
|
&(&1.status == :completed),
|
||||||
|
5_000
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Enum.all?(tasks, &(&1.group_id == result.task_group_id))
|
||||||
|
assert Enum.all?(tasks, &(&1.group_name == "Apply Site Validation"))
|
||||||
|
|
||||||
|
validation_task = Enum.find(tasks, &(&1.name == "Validate Site"))
|
||||||
|
assert validation_task.result.kind == "open_editor"
|
||||||
|
assert validation_task.result.route == "site_validation"
|
||||||
|
assert validation_task.result.payload.missing_url_paths == []
|
||||||
|
assert validation_task.result.payload.extra_url_paths == []
|
||||||
|
assert validation_task.result.payload.updated_post_url_paths == []
|
||||||
|
end
|
||||||
|
|
||||||
test "metadata_diff queues a tracked maintenance task and returns the report as an editor payload" do
|
test "metadata_diff queues a tracked maintenance task and returns the report as an editor payload" do
|
||||||
assert {:ok, result} = ShellCommands.execute("metadata_diff")
|
assert {:ok, result} = ShellCommands.execute("metadata_diff")
|
||||||
|
|
||||||
|
|||||||
@@ -914,6 +914,321 @@ defmodule BDS.GenerationTest do
|
|||||||
assert clean_report.updated_post_url_paths == []
|
assert clean_report.updated_post_url_paths == []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "apply_validation renders only targeted routes and leaves feed/atom/404/pagefind untouched",
|
||||||
|
%{project: project, temp_dir: temp_dir} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
Metadata.update_project_metadata(project.id, %{
|
||||||
|
public_url: "https://example.com/blog",
|
||||||
|
main_language: "en",
|
||||||
|
blog_languages: ["en"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, post_a} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "First Post",
|
||||||
|
content: "First body",
|
||||||
|
language: "en"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, _published_a} = Posts.publish_post(post_a.id)
|
||||||
|
|
||||||
|
assert {:ok, _result} =
|
||||||
|
BDS.Generation.generate_site(project.id, [:core, :single, :category, :tag, :date])
|
||||||
|
|
||||||
|
feed_path = Path.join([temp_dir, "html", "feed.xml"])
|
||||||
|
atom_path = Path.join([temp_dir, "html", "atom.xml"])
|
||||||
|
not_found_path = Path.join([temp_dir, "html", "404.html"])
|
||||||
|
pagefind_path = Path.join([temp_dir, "html", "pagefind", "index.json"])
|
||||||
|
|
||||||
|
feed_before = File.read!(feed_path)
|
||||||
|
atom_before = File.read!(atom_path)
|
||||||
|
not_found_before = File.read!(not_found_path)
|
||||||
|
pagefind_before = File.read!(pagefind_path)
|
||||||
|
|
||||||
|
# Publishing a new post leaves the generated feed/index reflecting only post A
|
||||||
|
# until validation apply targets the newly missing routes.
|
||||||
|
assert {:ok, post_b} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Second Post",
|
||||||
|
content: "Second body",
|
||||||
|
language: "en"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_b} = Posts.publish_post(post_b.id)
|
||||||
|
|
||||||
|
post_b_path = BDS.Generation.post_output_path(published_b)
|
||||||
|
post_b_file = Path.join([temp_dir, "html", post_b_path])
|
||||||
|
refute File.exists?(post_b_file)
|
||||||
|
|
||||||
|
assert {:ok, report} = BDS.Generation.validate_site(project.id)
|
||||||
|
assert relative_path_to_url_path(post_b_path) in report.missing_url_paths
|
||||||
|
|
||||||
|
assert {:ok, repair} = BDS.Generation.apply_validation(project.id, report)
|
||||||
|
assert repair.rendered_url_count > 0
|
||||||
|
|
||||||
|
# The targeted single-post route is rendered.
|
||||||
|
assert File.exists?(post_b_file)
|
||||||
|
|
||||||
|
# Old-app parity: feed, atom, 404, and the search index are NOT regenerated on apply.
|
||||||
|
assert File.read!(feed_path) == feed_before
|
||||||
|
assert File.read!(atom_path) == atom_before
|
||||||
|
assert File.read!(not_found_path) == not_found_before
|
||||||
|
assert File.read!(pagefind_path) == pagefind_before
|
||||||
|
end
|
||||||
|
|
||||||
|
test "apply_validation re-renders only the affected post's categories, tags, and single route",
|
||||||
|
%{project: project, temp_dir: temp_dir} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
Metadata.update_project_metadata(project.id, %{
|
||||||
|
public_url: "https://example.com/blog",
|
||||||
|
main_language: "en",
|
||||||
|
blog_languages: ["en"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, post_a} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Alpha",
|
||||||
|
content: "Alpha body",
|
||||||
|
language: "en",
|
||||||
|
categories: ["notes"],
|
||||||
|
tags: ["elixir"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_a} = Posts.publish_post(post_a.id)
|
||||||
|
|
||||||
|
assert {:ok, post_b} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Beta",
|
||||||
|
content: "Beta body",
|
||||||
|
language: "en",
|
||||||
|
categories: ["photos"],
|
||||||
|
tags: ["erlang"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_b} = Posts.publish_post(post_b.id)
|
||||||
|
|
||||||
|
assert {:ok, _result} =
|
||||||
|
BDS.Generation.generate_site(project.id, [:core, :single, :category, :tag, :date])
|
||||||
|
|
||||||
|
post_a_path = BDS.Generation.post_output_path(published_a)
|
||||||
|
post_b_path = BDS.Generation.post_output_path(published_b)
|
||||||
|
|
||||||
|
notes_before = generated_updated_at(project.id, "category/notes/index.html")
|
||||||
|
elixir_before = generated_updated_at(project.id, "tag/elixir/index.html")
|
||||||
|
photos_before = generated_updated_at(project.id, "category/photos/index.html")
|
||||||
|
erlang_before = generated_updated_at(project.id, "tag/erlang/index.html")
|
||||||
|
post_b_before = generated_updated_at(project.id, post_b_path)
|
||||||
|
|
||||||
|
File.rm!(Path.join([temp_dir, "html", post_a_path]))
|
||||||
|
|
||||||
|
assert {:ok, report} = BDS.Generation.validate_site(project.id)
|
||||||
|
assert relative_path_to_url_path(post_a_path) in report.missing_url_paths
|
||||||
|
# Only post A's route is affected; post B must not be flagged as updated.
|
||||||
|
assert report.updated_post_url_paths == []
|
||||||
|
|
||||||
|
assert {:ok, _repair} = BDS.Generation.apply_validation(project.id, report)
|
||||||
|
|
||||||
|
# Post A's own single route plus only its category and tag archives are re-rendered.
|
||||||
|
assert File.exists?(Path.join([temp_dir, "html", post_a_path]))
|
||||||
|
assert generated_updated_at(project.id, "category/notes/index.html") > notes_before
|
||||||
|
assert generated_updated_at(project.id, "tag/elixir/index.html") > elixir_before
|
||||||
|
|
||||||
|
# Post B's single route and its unrelated category and tag archives are left untouched.
|
||||||
|
assert generated_updated_at(project.id, post_b_path) == post_b_before
|
||||||
|
assert generated_updated_at(project.id, "category/photos/index.html") == photos_before
|
||||||
|
assert generated_updated_at(project.id, "tag/erlang/index.html") == erlang_before
|
||||||
|
end
|
||||||
|
|
||||||
|
test "apply_validation streams one progress message per rewritten URL with a running count",
|
||||||
|
%{project: project, temp_dir: temp_dir} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
Metadata.update_project_metadata(project.id, %{
|
||||||
|
public_url: "https://example.com/blog",
|
||||||
|
main_language: "en",
|
||||||
|
blog_languages: ["en"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Progress Post",
|
||||||
|
content: "Progress body",
|
||||||
|
language: "en",
|
||||||
|
categories: ["notes"],
|
||||||
|
tags: ["elixir"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published} = Posts.publish_post(post.id)
|
||||||
|
|
||||||
|
assert {:ok, _result} =
|
||||||
|
BDS.Generation.generate_site(project.id, [:core, :single, :category, :tag, :date])
|
||||||
|
|
||||||
|
post_path = BDS.Generation.post_output_path(published)
|
||||||
|
File.rm!(Path.join([temp_dir, "html", post_path]))
|
||||||
|
|
||||||
|
assert {:ok, report} = BDS.Generation.validate_site(project.id)
|
||||||
|
|
||||||
|
test_pid = self()
|
||||||
|
|
||||||
|
assert {:ok, _repair} =
|
||||||
|
BDS.Generation.apply_validation(project.id, report,
|
||||||
|
on_progress: fn progress, message ->
|
||||||
|
send(test_pid, {:progress, progress, message})
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
messages = collect_progress_messages([])
|
||||||
|
post_url = relative_path_to_url_path(post_path)
|
||||||
|
|
||||||
|
# The rewritten single-post URL is named in a progress message (like the old app).
|
||||||
|
assert Enum.any?(messages, fn {_progress, message} -> String.contains?(message, post_url) end)
|
||||||
|
|
||||||
|
# Progress messages carry a running "(rewritten/total)" count of URLs.
|
||||||
|
assert Enum.any?(messages, fn {_progress, message} -> message =~ ~r/\(\d+\/\d+\)/ end)
|
||||||
|
|
||||||
|
# Progress advances incrementally rather than jumping straight from 0.0 to 1.0.
|
||||||
|
fractions =
|
||||||
|
messages
|
||||||
|
|> Enum.map(fn {progress, _message} -> progress end)
|
||||||
|
|> Enum.filter(fn progress -> progress > 0.0 and progress < 1.0 end)
|
||||||
|
|
||||||
|
assert fractions != []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "prepare_validation_apply classifies page and day archive routes without section fallback",
|
||||||
|
%{project: project} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
Metadata.update_project_metadata(project.id, %{
|
||||||
|
public_url: "https://example.com/blog",
|
||||||
|
main_language: "en",
|
||||||
|
blog_languages: ["en"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, page} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "About",
|
||||||
|
content: "About body",
|
||||||
|
language: "en",
|
||||||
|
categories: ["page"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_page} = Posts.publish_post(page.id)
|
||||||
|
|
||||||
|
assert {:ok, dated_post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Day Archive Source",
|
||||||
|
content: "Day archive body",
|
||||||
|
language: "en"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_dated_post} = Posts.publish_post(dated_post.id)
|
||||||
|
|
||||||
|
{year, month, day} =
|
||||||
|
published_dated_post.created_at
|
||||||
|
|> DateTime.from_unix!(:millisecond)
|
||||||
|
|> then(&{&1.year, &1.month, &1.day})
|
||||||
|
|
||||||
|
assert {:ok, page_preparation} =
|
||||||
|
BDS.Generation.prepare_validation_apply(project.id, %{
|
||||||
|
missing_url_paths: ["/#{published_page.slug}"],
|
||||||
|
updated_post_url_paths: [],
|
||||||
|
extra_url_paths: []
|
||||||
|
})
|
||||||
|
|
||||||
|
assert page_preparation.sections_to_render == [:core]
|
||||||
|
|
||||||
|
assert {:ok, day_preparation} =
|
||||||
|
BDS.Generation.prepare_validation_apply(project.id, %{
|
||||||
|
missing_url_paths: [
|
||||||
|
"/#{year}/#{String.pad_leading(Integer.to_string(month), 2, "0")}/#{String.pad_leading(Integer.to_string(day), 2, "0")}"
|
||||||
|
],
|
||||||
|
updated_post_url_paths: [],
|
||||||
|
extra_url_paths: []
|
||||||
|
})
|
||||||
|
|
||||||
|
assert day_preparation.sections_to_render == [:date]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "apply_validation_section reports visible progress before section rendering starts", %{
|
||||||
|
project: project
|
||||||
|
} do
|
||||||
|
assert {:ok, _metadata} =
|
||||||
|
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: "Section Progress Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ labels.site_search_label }}: {{ post.title }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_template} = BDS.Templates.publish_template(template.id)
|
||||||
|
|
||||||
|
published_posts =
|
||||||
|
Enum.map(1..3, fn index ->
|
||||||
|
assert {:ok, post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Section Progress Post #{index}",
|
||||||
|
content: "Section progress body #{index}",
|
||||||
|
template_slug: published_template.slug
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published_post} = Posts.publish_post(post.id)
|
||||||
|
published_post
|
||||||
|
end)
|
||||||
|
|
||||||
|
post_url_paths =
|
||||||
|
Enum.map(
|
||||||
|
published_posts,
|
||||||
|
&(BDS.Generation.post_output_path(&1) |> relative_path_to_url_path())
|
||||||
|
)
|
||||||
|
|
||||||
|
parent = self()
|
||||||
|
|
||||||
|
on_progress = fn value, message ->
|
||||||
|
send(parent, {:apply_section_progress, value, message})
|
||||||
|
end
|
||||||
|
|
||||||
|
assert {:ok, _result} =
|
||||||
|
BDS.Generation.apply_validation_section(
|
||||||
|
project.id,
|
||||||
|
%{
|
||||||
|
missing_url_paths: post_url_paths,
|
||||||
|
updated_post_url_paths: [],
|
||||||
|
extra_url_paths: []
|
||||||
|
},
|
||||||
|
:single,
|
||||||
|
on_progress: on_progress
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_receive {:apply_section_progress, +0.0, "Render Single Posts"}
|
||||||
|
|
||||||
|
assert_receive {:apply_section_progress, progress, message}
|
||||||
|
when progress > 0.0 and progress < 1.0 and
|
||||||
|
is_binary(message)
|
||||||
|
|
||||||
|
rendered_path =
|
||||||
|
Path.join([
|
||||||
|
project.data_path,
|
||||||
|
"html",
|
||||||
|
BDS.Generation.post_output_path(List.first(published_posts))
|
||||||
|
])
|
||||||
|
|
||||||
|
assert File.read!(rendered_path) =~ "Site search: Section Progress Post"
|
||||||
|
end
|
||||||
|
|
||||||
test "apply_validation returns an error for unreadable generated files", %{
|
test "apply_validation returns an error for unreadable generated files", %{
|
||||||
project: project,
|
project: project,
|
||||||
temp_dir: temp_dir
|
temp_dir: temp_dir
|
||||||
@@ -1343,6 +1658,23 @@ defmodule BDS.GenerationTest do
|
|||||||
# Pushes generation timestamps and html mtimes far into the past so a
|
# Pushes generation timestamps and html mtimes far into the past so a
|
||||||
# subsequent source-file write is newer by more than the second-granularity
|
# subsequent source-file write is newer by more than the second-granularity
|
||||||
# mtime tolerance, without sleeping across real second boundaries.
|
# mtime tolerance, without sleeping across real second boundaries.
|
||||||
|
defp collect_progress_messages(acc) do
|
||||||
|
receive do
|
||||||
|
{:progress, progress, message} -> collect_progress_messages([{progress, message} | acc])
|
||||||
|
after
|
||||||
|
0 -> Enum.reverse(acc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp generated_updated_at(project_id, relative_path) do
|
||||||
|
BDS.Generation.GeneratedFileHash
|
||||||
|
|> Repo.get_by(project_id: project_id, relative_path: relative_path)
|
||||||
|
|> case do
|
||||||
|
nil -> flunk("expected generated file hash for #{relative_path}")
|
||||||
|
%{updated_at: updated_at} -> updated_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp backdate_generated_outputs(project_id, temp_dir) do
|
defp backdate_generated_outputs(project_id, temp_dir) do
|
||||||
past_posix = System.os_time(:second) - 120
|
past_posix = System.os_time(:second) - 120
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user