Reopen published code only for content changes.
This commit is contained in:
@@ -10,7 +10,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
|||||||
- Post and translation authoring with change-aware draft/published/archive lifecycle, file-backed change discard, canonical draft reopening after manual translation edits, non-disruptive automatic translation, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, live link/backlink graphs, media, and batch gallery-image import.
|
- Post and translation authoring with change-aware draft/published/archive lifecycle, file-backed change discard, canonical draft reopening after manual translation edits, non-disruptive automatic translation, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, live link/backlink graphs, media, and batch gallery-image import.
|
||||||
- Media import, thumbnails, metadata translations, filters, validation, post assignment, and sequential drag-and-drop insertion into post editors.
|
- Media import, thumbnails, metadata translations, filters, validation, post assignment, and sequential drag-and-drop insertion into post editors.
|
||||||
- WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping.
|
- WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping.
|
||||||
- Post, Liquid template, and Lua script editing with dedicated syntax highlighting, explicit syntax-check feedback, normalized collision-safe template/script slug changes, reference-safe template renames, and publish-time enforcement of the bDS2 Liquid tag/filter/operator subset, using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms, including airplane-gated Git sync.
|
- Post, Liquid template, and Lua script editing with dedicated syntax highlighting, explicit syntax-check feedback, change-aware published/draft lifecycle, normalized collision-safe template/script slug changes, reference-safe template renames, and publish-time enforcement of the bDS2 Liquid tag/filter/operator subset, using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms, including airplane-gated Git sync.
|
||||||
- SQLite and filesystem persistence with frontmatter, relationship-safe media sidecars, rebuild, metadata diff/repair, stale post-path cleanup on republish, bDS2-compatible checksums and NFD slug generation, and FTS5 search.
|
- SQLite and filesystem persistence with frontmatter, relationship-safe media sidecars, rebuild, metadata diff/repair, stale post-path cleanup on republish, bDS2-compatible checksums and NFD slug generation, and FTS5 search.
|
||||||
- Optional on-device multilingual semantic search and similar-post tag suggestions backed by a persistent USearch index, plus always-available partial-name tag autocomplete and duplicate-post review in the desktop workspace.
|
- Optional on-device multilingual semantic search and similar-post tag suggestions backed by a persistent USearch index, plus always-available partial-name tag autocomplete and duplicate-post review in the desktop workspace.
|
||||||
- Read-only in-app browsers in the Help menu for the bundled global `DOCUMENTATION.md`, the generated Lua API reference with public types and runnable examples, the [CLI/server/TUI documentation](CLI.md), and the [MCP server documentation](MCP.md), with safe GFM rendering and confirmed external links.
|
- Read-only in-app browsers in the Help menu for the bundled global `DOCUMENTATION.md`, the generated Lua API reference with public types and runnable examples, the [CLI/server/TUI documentation](CLI.md), and the [MCP server documentation](MCP.md), with safe GFM rendering and confirmed external links.
|
||||||
|
|||||||
@@ -81,6 +81,15 @@ pub fn update_script(
|
|||||||
}
|
}
|
||||||
let original_slug = script.slug.clone();
|
let original_slug = script.slug.clone();
|
||||||
let original_file_path = script.file_path.clone();
|
let original_file_path = script.file_path.clone();
|
||||||
|
let was_published = script.status == ScriptStatus::Published;
|
||||||
|
let effective_content = script.content.clone().unwrap_or_else(|| {
|
||||||
|
if original_file_path.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
read_published_script_body(data_dir, &original_file_path)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let content_changed = content.is_some_and(|new_content| new_content != effective_content);
|
||||||
|
|
||||||
if let Some(requested_slug) = slug {
|
if let Some(requested_slug) = slug {
|
||||||
let slug = slugify(requested_slug);
|
let slug = slugify(requested_slug);
|
||||||
@@ -112,17 +121,18 @@ pub fn update_script(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let slug_changed = script.slug != original_slug;
|
let slug_changed = script.slug != original_slug;
|
||||||
let published_body = if slug_changed && !original_file_path.is_empty() {
|
let published_body = if !original_file_path.is_empty()
|
||||||
Some(read_published_script_body(data_dir, &original_file_path))
|
&& (slug_changed || (was_published && !content_changed))
|
||||||
|
{
|
||||||
|
Some(effective_content)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if published_body.is_some() {
|
if slug_changed && published_body.is_some() {
|
||||||
script.file_path = script_file_path(&script.slug);
|
script.file_path = script_file_path(&script.slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If published, transition back to draft on edit
|
if was_published && content_changed {
|
||||||
if script.status == ScriptStatus::Published {
|
|
||||||
script.status = ScriptStatus::Draft;
|
script.status = ScriptStatus::Draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,17 +147,34 @@ pub fn update_script(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Save script content (editor save). Updates DB, bumps version.
|
/// Save script content (editor save). Updates DB, bumps version.
|
||||||
pub fn save_script(conn: &Connection, script_id: &str, content: &str) -> EngineResult<Script> {
|
pub fn save_script(
|
||||||
|
conn: &Connection,
|
||||||
|
data_dir: &Path,
|
||||||
|
script_id: &str,
|
||||||
|
content: &str,
|
||||||
|
) -> EngineResult<Script> {
|
||||||
let mut script = qs::get_script_by_id(conn, script_id)?;
|
let mut script = qs::get_script_by_id(conn, script_id)?;
|
||||||
|
let was_published = script.status == ScriptStatus::Published;
|
||||||
|
let effective_content = script.content.clone().unwrap_or_else(|| {
|
||||||
|
if script.file_path.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
read_published_script_body(data_dir, &script.file_path)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let content_changed = content != effective_content;
|
||||||
script.content = Some(content.to_string());
|
script.content = Some(content.to_string());
|
||||||
script.version += 1;
|
script.version += 1;
|
||||||
script.updated_at = now_unix_ms();
|
script.updated_at = now_unix_ms();
|
||||||
|
|
||||||
if script.status == ScriptStatus::Published {
|
if was_published && content_changed {
|
||||||
script.status = ScriptStatus::Draft;
|
script.status = ScriptStatus::Draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
qs::update_script(conn, &script)?;
|
qs::update_script(conn, &script)?;
|
||||||
|
if was_published && !content_changed && !script.file_path.is_empty() {
|
||||||
|
rewrite_renamed_script_file(data_dir, &script.file_path, &script, &effective_content)?;
|
||||||
|
}
|
||||||
emit_script(&script, NotificationAction::Updated);
|
emit_script(&script, NotificationAction::Updated);
|
||||||
Ok(script)
|
Ok(script)
|
||||||
}
|
}
|
||||||
@@ -494,13 +521,114 @@ mod tests {
|
|||||||
let (frontmatter, body) = crate::util::frontmatter::read_script_file(&new_file).unwrap();
|
let (frontmatter, body) = crate::util::frontmatter::read_script_file(&new_file).unwrap();
|
||||||
assert_eq!(frontmatter.slug, "renamed-script");
|
assert_eq!(frontmatter.slug, "renamed-script");
|
||||||
assert_eq!(body, "function main()\nend");
|
assert_eq!(body, "function main()\nend");
|
||||||
|
assert_eq!(updated.status, ScriptStatus::Published);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn published_script_metadata_update_stays_published_and_rewrites_frontmatter() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let script = create_script(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Script",
|
||||||
|
ScriptKind::Utility,
|
||||||
|
"function main()\nend",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_script(db.conn(), dir.path(), &script.id).unwrap();
|
||||||
|
|
||||||
|
let updated = update_script(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
&script.id,
|
||||||
|
"p1",
|
||||||
|
Some("Renamed title"),
|
||||||
|
None,
|
||||||
|
Some(ScriptKind::Transform),
|
||||||
|
Some("transform"),
|
||||||
|
Some(false),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(updated.status, ScriptStatus::Published);
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (frontmatter, body) = crate::util::frontmatter::read_script_file(&file).unwrap();
|
||||||
|
assert_eq!(frontmatter.title, "Renamed title");
|
||||||
|
assert_eq!(frontmatter.kind, "transform");
|
||||||
|
assert_eq!(frontmatter.entrypoint, "transform");
|
||||||
|
assert!(!frontmatter.enabled);
|
||||||
|
assert_eq!(frontmatter.version, updated.version);
|
||||||
|
assert_eq!(body, "function main()\nend");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn published_script_content_change_reopens_draft_and_preserves_published_file() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let script = create_script(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Script",
|
||||||
|
ScriptKind::Utility,
|
||||||
|
"function main()\n return 'old'\nend",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_script(db.conn(), dir.path(), &script.id).unwrap();
|
||||||
|
|
||||||
|
let updated = update_script(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
&script.id,
|
||||||
|
"p1",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some("function main()\n return 'new'\nend"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(updated.status, ScriptStatus::Draft);
|
||||||
|
assert_eq!(
|
||||||
|
updated.content.as_deref(),
|
||||||
|
Some("function main()\n return 'new'\nend")
|
||||||
|
);
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (_, body) = crate::util::frontmatter::read_script_file(&file).unwrap();
|
||||||
|
assert_eq!(body, "function main()\n return 'old'\nend");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identical_published_script_save_stays_published() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let script = create_script(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Script",
|
||||||
|
ScriptKind::Utility,
|
||||||
|
"function main()\nend",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_script(db.conn(), dir.path(), &script.id).unwrap();
|
||||||
|
|
||||||
|
let saved = save_script(db.conn(), dir.path(), &script.id, "function main()\nend").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(saved.status, ScriptStatus::Published);
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (frontmatter, body) = crate::util::frontmatter::read_script_file(&file).unwrap();
|
||||||
|
assert_eq!(frontmatter.version, saved.version);
|
||||||
|
assert_eq!(body, "function main()\nend");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn save_script_updates_content() {
|
fn save_script_updates_content() {
|
||||||
let (db, _dir) = setup();
|
let (db, dir) = setup();
|
||||||
let s = create_script(db.conn(), "p1", "S", ScriptKind::Macro, "old", None).unwrap();
|
let s = create_script(db.conn(), "p1", "S", ScriptKind::Macro, "old", None).unwrap();
|
||||||
let saved = save_script(db.conn(), &s.id, "new body").unwrap();
|
let saved = save_script(db.conn(), dir.path(), &s.id, "new body").unwrap();
|
||||||
assert_eq!(saved.content, Some("new body".to_string()));
|
assert_eq!(saved.content, Some("new body".to_string()));
|
||||||
assert_eq!(saved.version, 2);
|
assert_eq!(saved.version, 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,15 @@ pub fn update_template(
|
|||||||
}
|
}
|
||||||
let original_slug = tpl.slug.clone();
|
let original_slug = tpl.slug.clone();
|
||||||
let original_file_path = tpl.file_path.clone();
|
let original_file_path = tpl.file_path.clone();
|
||||||
|
let was_published = tpl.status == TemplateStatus::Published;
|
||||||
|
let effective_content = tpl.content.clone().unwrap_or_else(|| {
|
||||||
|
if original_file_path.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
read_published_template_body(data_dir, &original_file_path)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let content_changed = content.is_some_and(|new_content| new_content != effective_content);
|
||||||
|
|
||||||
if let Some(requested_slug) = slug {
|
if let Some(requested_slug) = slug {
|
||||||
let slug = slugify(requested_slug);
|
let slug = slugify(requested_slug);
|
||||||
@@ -118,23 +127,19 @@ pub fn update_template(
|
|||||||
tpl.content = Some(c.to_string());
|
tpl.content = Some(c.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If published, transition back to draft on edit
|
if was_published && content_changed {
|
||||||
if tpl.status == TemplateStatus::Published {
|
|
||||||
// Reload content from file if needed
|
|
||||||
if tpl.content.is_none() && !tpl.file_path.is_empty() {
|
|
||||||
// Content will come from the file when we read for publish
|
|
||||||
// For update we need the new content from the caller
|
|
||||||
}
|
|
||||||
tpl.status = TemplateStatus::Draft;
|
tpl.status = TemplateStatus::Draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slug_changed = tpl.slug != original_slug;
|
let slug_changed = tpl.slug != original_slug;
|
||||||
let published_body = if slug_changed && !original_file_path.is_empty() {
|
let published_body = if !original_file_path.is_empty()
|
||||||
Some(read_published_template_body(data_dir, &original_file_path))
|
&& (slug_changed || (was_published && !content_changed))
|
||||||
|
{
|
||||||
|
Some(effective_content)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if published_body.is_some() {
|
if slug_changed && published_body.is_some() {
|
||||||
tpl.file_path = template_file_path(&tpl.slug);
|
tpl.file_path = template_file_path(&tpl.slug);
|
||||||
}
|
}
|
||||||
tpl.version += 1;
|
tpl.version += 1;
|
||||||
@@ -198,20 +203,32 @@ pub fn update_template(
|
|||||||
/// Save template content (editor save). Updates DB, bumps version.
|
/// Save template content (editor save). Updates DB, bumps version.
|
||||||
pub fn save_template(
|
pub fn save_template(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
|
data_dir: &Path,
|
||||||
template_id: &str,
|
template_id: &str,
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> EngineResult<Template> {
|
) -> EngineResult<Template> {
|
||||||
let mut tpl = qt::get_template_by_id(conn, template_id)?;
|
let mut tpl = qt::get_template_by_id(conn, template_id)?;
|
||||||
|
let was_published = tpl.status == TemplateStatus::Published;
|
||||||
|
let effective_content = tpl.content.clone().unwrap_or_else(|| {
|
||||||
|
if tpl.file_path.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
read_published_template_body(data_dir, &tpl.file_path)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let content_changed = content != effective_content;
|
||||||
tpl.content = Some(content.to_string());
|
tpl.content = Some(content.to_string());
|
||||||
tpl.version += 1;
|
tpl.version += 1;
|
||||||
tpl.updated_at = now_unix_ms();
|
tpl.updated_at = now_unix_ms();
|
||||||
|
|
||||||
// If published, transition back to draft
|
if was_published && content_changed {
|
||||||
if tpl.status == TemplateStatus::Published {
|
|
||||||
tpl.status = TemplateStatus::Draft;
|
tpl.status = TemplateStatus::Draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
qt::update_template(conn, &tpl)?;
|
qt::update_template(conn, &tpl)?;
|
||||||
|
if was_published && !content_changed && !tpl.file_path.is_empty() {
|
||||||
|
rewrite_renamed_template_file(data_dir, &tpl.file_path, &tpl, &effective_content)?;
|
||||||
|
}
|
||||||
emit_template(&tpl, NotificationAction::Updated);
|
emit_template(&tpl, NotificationAction::Updated);
|
||||||
Ok(tpl)
|
Ok(tpl)
|
||||||
}
|
}
|
||||||
@@ -809,6 +826,98 @@ mod tests {
|
|||||||
let (frontmatter, body) = crate::util::frontmatter::read_template_file(&new_file).unwrap();
|
let (frontmatter, body) = crate::util::frontmatter::read_template_file(&new_file).unwrap();
|
||||||
assert_eq!(frontmatter.slug, "renamed-template");
|
assert_eq!(frontmatter.slug, "renamed-template");
|
||||||
assert_eq!(body, "<article>{{ title }}</article>");
|
assert_eq!(body, "<article>{{ title }}</article>");
|
||||||
|
assert_eq!(updated.status, TemplateStatus::Published);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn published_template_metadata_update_stays_published_and_rewrites_frontmatter() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let template = create_template(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Template",
|
||||||
|
TemplateKind::Post,
|
||||||
|
"<article>{{ title }}</article>",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_template(db.conn(), dir.path(), &template.id).unwrap();
|
||||||
|
|
||||||
|
let updated = update_template(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
&template.id,
|
||||||
|
"p1",
|
||||||
|
Some("Renamed title"),
|
||||||
|
None,
|
||||||
|
Some(TemplateKind::List),
|
||||||
|
Some(false),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(updated.status, TemplateStatus::Published);
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (frontmatter, body) = crate::util::frontmatter::read_template_file(&file).unwrap();
|
||||||
|
assert_eq!(frontmatter.title, "Renamed title");
|
||||||
|
assert_eq!(frontmatter.kind, "list");
|
||||||
|
assert!(!frontmatter.enabled);
|
||||||
|
assert_eq!(frontmatter.version, updated.version);
|
||||||
|
assert_eq!(body, "<article>{{ title }}</article>");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn published_template_content_change_reopens_draft_and_preserves_published_file() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let template = create_template(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Template",
|
||||||
|
TemplateKind::Post,
|
||||||
|
"old body",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_template(db.conn(), dir.path(), &template.id).unwrap();
|
||||||
|
|
||||||
|
let updated = update_template(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
&template.id,
|
||||||
|
"p1",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some("new body"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(updated.status, TemplateStatus::Draft);
|
||||||
|
assert_eq!(updated.content.as_deref(), Some("new body"));
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (_, body) = crate::util::frontmatter::read_template_file(&file).unwrap();
|
||||||
|
assert_eq!(body, "old body");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn identical_published_template_save_stays_published() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let template = create_template(
|
||||||
|
db.conn(),
|
||||||
|
"p1",
|
||||||
|
"Published Template",
|
||||||
|
TemplateKind::Post,
|
||||||
|
"same",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_template(db.conn(), dir.path(), &template.id).unwrap();
|
||||||
|
|
||||||
|
let saved = save_template(db.conn(), dir.path(), &template.id, "same").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(saved.status, TemplateStatus::Published);
|
||||||
|
let file = fs::read_to_string(dir.path().join(&published.file_path)).unwrap();
|
||||||
|
let (frontmatter, body) = crate::util::frontmatter::read_template_file(&file).unwrap();
|
||||||
|
assert_eq!(frontmatter.version, saved.version);
|
||||||
|
assert_eq!(body, "same");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -956,9 +1065,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn save_template_updates_content() {
|
fn save_template_updates_content() {
|
||||||
let (db, _dir) = setup();
|
let (db, dir) = setup();
|
||||||
let tpl = create_template(db.conn(), "p1", "Tpl", TemplateKind::Post, "old").unwrap();
|
let tpl = create_template(db.conn(), "p1", "Tpl", TemplateKind::Post, "old").unwrap();
|
||||||
let saved = save_template(db.conn(), &tpl.id, "new content").unwrap();
|
let saved = save_template(db.conn(), dir.path(), &tpl.id, "new content").unwrap();
|
||||||
assert_eq!(saved.content, Some("new content".to_string()));
|
assert_eq!(saved.content, Some("new content".to_string()));
|
||||||
assert_eq!(saved.version, 2);
|
assert_eq!(saved.version, 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,10 @@ rule UpdateScript {
|
|||||||
ensures: script.file_path = format("scripts/{slug}.lua", slug: script.slug)
|
ensures: script.file_path = format("scripts/{slug}.lua", slug: script.slug)
|
||||||
ensures: ScriptFileWritten(script)
|
ensures: ScriptFileWritten(script)
|
||||||
ensures: PreviousScriptFileDeleted(script)
|
ensures: PreviousScriptFileDeleted(script)
|
||||||
|
if script.status = published and not script_changes_affect_published_output(changes):
|
||||||
|
ensures: script.status = published
|
||||||
|
ensures: ScriptFileWritten(script)
|
||||||
|
-- Keep published frontmatter synchronized for metadata-only and no-op content updates.
|
||||||
ensures: script.updated_at = now
|
ensures: script.updated_at = now
|
||||||
ensures: script.version = script.version + 1
|
ensures: script.version = script.version + 1
|
||||||
}
|
}
|
||||||
@@ -237,7 +241,8 @@ rule UpdateScript {
|
|||||||
rule ReopenPublishedScript {
|
rule ReopenPublishedScript {
|
||||||
when: UpdateScriptRequested(script, changes)
|
when: UpdateScriptRequested(script, changes)
|
||||||
requires: script.status = published
|
requires: script.status = published
|
||||||
requires: script_changes_affect_published_output(changes)
|
requires: changes.content != null
|
||||||
|
requires: changes.content != effective_script_content(script)
|
||||||
ensures: script.status = draft
|
ensures: script.status = draft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ rule UpdateTemplate {
|
|||||||
ensures: template.file_path = format("templates/{slug}.liquid", slug: template.slug)
|
ensures: template.file_path = format("templates/{slug}.liquid", slug: template.slug)
|
||||||
ensures: TemplateFileWritten(template)
|
ensures: TemplateFileWritten(template)
|
||||||
ensures: PreviousTemplateFileDeleted(template)
|
ensures: PreviousTemplateFileDeleted(template)
|
||||||
|
if template.status = published and not template_changes_affect_rendered_output(changes):
|
||||||
|
ensures: template.status = published
|
||||||
|
ensures: TemplateFileWritten(template)
|
||||||
|
-- Keep published frontmatter synchronized for metadata-only and no-op content updates.
|
||||||
ensures: template.updated_at = now
|
ensures: template.updated_at = now
|
||||||
ensures: template.version = template.version + 1
|
ensures: template.version = template.version + 1
|
||||||
}
|
}
|
||||||
@@ -142,7 +146,8 @@ rule UpdateTemplate {
|
|||||||
rule ReopenPublishedTemplate {
|
rule ReopenPublishedTemplate {
|
||||||
when: UpdateTemplateRequested(template, changes)
|
when: UpdateTemplateRequested(template, changes)
|
||||||
requires: template.status = published
|
requires: template.status = published
|
||||||
requires: template_changes_affect_rendered_output(changes)
|
requires: changes.content != null
|
||||||
|
requires: changes.content != effective_template_content(template)
|
||||||
ensures: template.status = draft
|
ensures: template.status = draft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user