Preserve file-only content when archiving.
This commit is contained in:
@@ -7,7 +7,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
|||||||
## Available Features
|
## Available Features
|
||||||
|
|
||||||
- Native Iced desktop workspace with localized menus, tabs, anchored editor popovers, automatically paged post/media sidebars, dialogs, tasks, embedded Wry previews, and a live Pico CSS theme editor.
|
- Native Iced desktop workspace with localized menus, tabs, anchored editor popovers, automatically paged post/media sidebars, dialogs, tasks, embedded Wry previews, and a live Pico CSS theme editor.
|
||||||
- Post and translation authoring with change-aware draft/published/archive lifecycle, explicit unarchive, in-place published-frontmatter updates, metadata, tags, categories, links, media, and batch gallery-image import.
|
- Post and translation authoring with change-aware draft/published/archive lifecycle, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, links, 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 and explicit syntax-check feedback, including 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 and explicit syntax-check feedback, including 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.
|
||||||
|
|||||||
@@ -1035,7 +1035,7 @@ mod tests {
|
|||||||
use crate::db::queries::project::{insert_project, make_test_project};
|
use crate::db::queries::project::{insert_project, make_test_project};
|
||||||
use crate::db::queries::script::insert_script;
|
use crate::db::queries::script::insert_script;
|
||||||
use crate::db::queries::template::insert_template;
|
use crate::db::queries::template::insert_template;
|
||||||
use crate::engine::post::{create_post, publish_post};
|
use crate::engine::post::{archive_post, create_post, publish_post};
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
Media, Post, PostStatus, ProjectMetadata, Script, ScriptKind, ScriptStatus, Template,
|
Media, Post, PostStatus, ProjectMetadata, Script, ScriptKind, ScriptStatus, Template,
|
||||||
TemplateKind, TemplateStatus,
|
TemplateKind, TemplateStatus,
|
||||||
@@ -1095,6 +1095,58 @@ mod tests {
|
|||||||
assert!(report.orphans.is_empty(), "orphans: {:?}", report.orphans);
|
assert!(report.orphans.is_empty(), "orphans: {:?}", report.orphans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn archived_published_post_reports_only_bds2_frontmatter_drift() {
|
||||||
|
let (db, dir) = setup();
|
||||||
|
let post = create_post(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
"p1",
|
||||||
|
"Archived Post",
|
||||||
|
Some("body text"),
|
||||||
|
vec!["rust".into()],
|
||||||
|
vec!["tech".into()],
|
||||||
|
Some("Alice"),
|
||||||
|
Some("en"),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = publish_post(db.conn(), dir.path(), &post.id).unwrap();
|
||||||
|
let path = dir.path().join(&published.file_path);
|
||||||
|
let original_file = fs::read(&path).unwrap();
|
||||||
|
archive_post(db.conn(), dir.path(), &post.id).unwrap();
|
||||||
|
|
||||||
|
let report = compute_metadata_diff(db.conn(), dir.path(), "p1").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(fs::read(path).unwrap(), original_file);
|
||||||
|
assert!(
|
||||||
|
report
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.all(|error| !error.starts_with("post ")),
|
||||||
|
"post diff errors: {:?}",
|
||||||
|
report.errors
|
||||||
|
);
|
||||||
|
assert!(report.orphans.is_empty());
|
||||||
|
let diff = report
|
||||||
|
.diffs
|
||||||
|
.iter()
|
||||||
|
.find(|diff| diff.entity_type == "post" && diff.entity_id == post.id)
|
||||||
|
.expect("archived database status must differ from untouched published file");
|
||||||
|
assert!(diff.fields.iter().any(|field| {
|
||||||
|
field.field_name == "status"
|
||||||
|
&& field.db_value == "archived"
|
||||||
|
&& field.file_value == "published"
|
||||||
|
}));
|
||||||
|
assert!(
|
||||||
|
diff.fields
|
||||||
|
.iter()
|
||||||
|
.all(|field| matches!(field.field_name.as_str(), "status" | "updatedAt")),
|
||||||
|
"unexpected archived-post drift: {:?}",
|
||||||
|
diff.fields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detects_title_drift_in_post() {
|
fn detects_title_drift_in_post() {
|
||||||
let (db, dir) = setup();
|
let (db, dir) = setup();
|
||||||
|
|||||||
@@ -2375,12 +2375,15 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
publish_post(db.conn(), dir.path(), &post.id).unwrap();
|
let published = publish_post(db.conn(), dir.path(), &post.id).unwrap();
|
||||||
|
let post_path = dir.path().join(&published.file_path);
|
||||||
|
let published_file = fs::read(&post_path).unwrap();
|
||||||
archive_post(db.conn(), dir.path(), &post.id).unwrap();
|
archive_post(db.conn(), dir.path(), &post.id).unwrap();
|
||||||
|
|
||||||
let from_db = qp::get_post_by_id(db.conn(), &post.id).unwrap();
|
let from_db = qp::get_post_by_id(db.conn(), &post.id).unwrap();
|
||||||
assert_eq!(from_db.status, PostStatus::Archived);
|
assert_eq!(from_db.status, PostStatus::Archived);
|
||||||
assert_eq!(from_db.content, None);
|
assert_eq!(from_db.content, None);
|
||||||
|
assert_eq!(fs::read(post_path).unwrap(), published_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -126,6 +126,19 @@ fn reopened_draft_generation_uses_last_published_file() {
|
|||||||
assert_eq!(source.body_markdown, "Published body");
|
assert_eq!(source.body_markdown, "Published body");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn archived_post_with_published_file_is_not_a_generation_source() {
|
||||||
|
let (_db, dir) = setup();
|
||||||
|
let mut post = make_post("archived", 1_710_000_000_000);
|
||||||
|
write_published_snapshot(&dir, &mut post, "Published body");
|
||||||
|
post.status = PostStatus::Archived;
|
||||||
|
post.content = None;
|
||||||
|
|
||||||
|
let source = load_published_post_source(dir.path(), post).unwrap();
|
||||||
|
|
||||||
|
assert!(source.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validation_keeps_reopened_draft_published_snapshots() {
|
fn validation_keeps_reopened_draft_published_snapshots() {
|
||||||
let (db, dir) = setup();
|
let (db, dir) = setup();
|
||||||
|
|||||||
@@ -598,7 +598,12 @@ mod tests {
|
|||||||
update.as_slice(),
|
update.as_slice(),
|
||||||
[ServerMessage::Tasks { tasks, .. }] if tasks[0].progress == Some(0.5)
|
[ServerMessage::Tasks { tasks, .. }] if tasks[0].progress == Some(0.5)
|
||||||
));
|
));
|
||||||
assert!(session.pending().is_empty());
|
assert!(
|
||||||
|
session
|
||||||
|
.pending()
|
||||||
|
.iter()
|
||||||
|
.all(|message| !matches!(message, ServerMessage::Tasks { .. }))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -6896,12 +6896,44 @@ impl BdsApp {
|
|||||||
tab.is_dirty = false;
|
tab.is_dirty = false;
|
||||||
}
|
}
|
||||||
self.notify(ToastLevel::Success, &t(self.ui_locale, "editor.unarchived"));
|
self.notify(ToastLevel::Success, &t(self.ui_locale, "editor.unarchived"));
|
||||||
|
return self.refresh_sidebar_posts();
|
||||||
}
|
}
|
||||||
Err(error) => self.notify_operation_failed("editor.unarchive", error),
|
Err(error) => self.notify_operation_failed("editor.unarchive", error),
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn archive_post_editor(&mut self, post_id: &str) -> Task<Message> {
|
||||||
|
if let Err(error) = self.persist_post_editor_state(post_id) {
|
||||||
|
self.notify_operation_failed("editor.archive", error);
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
let (Some(db), Some(data_dir)) = (self.db.as_ref(), self.data_dir.as_ref()) else {
|
||||||
|
return Task::none();
|
||||||
|
};
|
||||||
|
if let Err(error) = engine::post::archive_post(db.conn(), data_dir, post_id) {
|
||||||
|
self.notify_operation_failed("editor.archive", error);
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
match bds_core::db::queries::post::get_post_by_id(db.conn(), post_id) {
|
||||||
|
Ok(post) => {
|
||||||
|
if let Some(editor) = self.post_editors.get_mut(post_id) {
|
||||||
|
editor.status = post.status;
|
||||||
|
editor.updated_at = post.updated_at;
|
||||||
|
editor.is_dirty = false;
|
||||||
|
editor.last_edit_at_ms = 0;
|
||||||
|
}
|
||||||
|
if let Some(tab) = self.tabs.iter_mut().find(|tab| tab.id == post_id) {
|
||||||
|
tab.is_dirty = false;
|
||||||
|
}
|
||||||
|
self.notify(ToastLevel::Success, &t(self.ui_locale, "editor.archived"));
|
||||||
|
return self.refresh_sidebar_posts();
|
||||||
|
}
|
||||||
|
Err(error) => self.notify_operation_failed("editor.archive", error),
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
fn delete_media_editor(&mut self, media_id: &str) -> Task<Message> {
|
fn delete_media_editor(&mut self, media_id: &str) -> Task<Message> {
|
||||||
let Some(db) = &self.db else {
|
let Some(db) = &self.db else {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
@@ -10910,6 +10942,50 @@ mod tests {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn post_editor_archive_action_keeps_published_body_file_only() {
|
||||||
|
let (db, project, tmp) = setup();
|
||||||
|
let created = post::create_post(
|
||||||
|
db.conn(),
|
||||||
|
tmp.path(),
|
||||||
|
&project.id,
|
||||||
|
"Published",
|
||||||
|
Some("File body"),
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
None,
|
||||||
|
Some("en"),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let published = post::publish_post(db.conn(), tmp.path(), &created.id).unwrap();
|
||||||
|
let path = tmp.path().join(&published.file_path);
|
||||||
|
let original_file = std::fs::read(&path).unwrap();
|
||||||
|
let mut app = make_app(db, project, &tmp);
|
||||||
|
open_post_editor(&mut app, &published);
|
||||||
|
app.post_editors
|
||||||
|
.get_mut(&created.id)
|
||||||
|
.unwrap()
|
||||||
|
.quick_actions_open = true;
|
||||||
|
|
||||||
|
let _ = app.handle_post_editor_msg(PostEditorMsg::Archive);
|
||||||
|
|
||||||
|
let from_db = bds_core::db::queries::post::get_post_by_id(
|
||||||
|
app.db.as_ref().unwrap().conn(),
|
||||||
|
&created.id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(from_db.status, PostStatus::Archived);
|
||||||
|
assert_eq!(from_db.content, None);
|
||||||
|
assert_eq!(std::fs::read(path).unwrap(), original_file);
|
||||||
|
assert_eq!(app.post_editors[&created.id].status, PostStatus::Archived);
|
||||||
|
assert!(!app.post_editors[&created.id].quick_actions_open);
|
||||||
|
assert!(app.toasts.iter().any(|toast| {
|
||||||
|
toast.level == ToastLevel::Success
|
||||||
|
&& toast.message == t(UiLocale::En, "editor.archived")
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preview_persist_bypasses_fts_for_translation_updates() {
|
fn preview_persist_bypasses_fts_for_translation_updates() {
|
||||||
let (db, project, tmp) = setup();
|
let (db, project, tmp) = setup();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ impl BdsApp {
|
|||||||
},
|
},
|
||||||
Save(String),
|
Save(String),
|
||||||
Publish(String),
|
Publish(String),
|
||||||
|
Archive(String),
|
||||||
Unarchive(String),
|
Unarchive(String),
|
||||||
Discard(String),
|
Discard(String),
|
||||||
ShowDelete {
|
ShowDelete {
|
||||||
@@ -209,7 +210,12 @@ impl BdsApp {
|
|||||||
PostEditorMsg::Publish => {
|
PostEditorMsg::Publish => {
|
||||||
deferred = DeferredPostAction::Publish(tab_id.clone());
|
deferred = DeferredPostAction::Publish(tab_id.clone());
|
||||||
}
|
}
|
||||||
|
PostEditorMsg::Archive => {
|
||||||
|
state.quick_actions_open = false;
|
||||||
|
deferred = DeferredPostAction::Archive(tab_id.clone());
|
||||||
|
}
|
||||||
PostEditorMsg::Unarchive => {
|
PostEditorMsg::Unarchive => {
|
||||||
|
state.quick_actions_open = false;
|
||||||
deferred = DeferredPostAction::Unarchive(tab_id.clone());
|
deferred = DeferredPostAction::Unarchive(tab_id.clone());
|
||||||
}
|
}
|
||||||
PostEditorMsg::Discard => {
|
PostEditorMsg::Discard => {
|
||||||
@@ -327,6 +333,7 @@ impl BdsApp {
|
|||||||
} => self.translate_post_to(&post_id, &target_language),
|
} => self.translate_post_to(&post_id, &target_language),
|
||||||
DeferredPostAction::Save(tab_id) => self.save_post_editor(&tab_id, true),
|
DeferredPostAction::Save(tab_id) => self.save_post_editor(&tab_id, true),
|
||||||
DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id),
|
DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id),
|
||||||
|
DeferredPostAction::Archive(tab_id) => self.archive_post_editor(&tab_id),
|
||||||
DeferredPostAction::Unarchive(tab_id) => self.unarchive_post_editor(&tab_id),
|
DeferredPostAction::Unarchive(tab_id) => self.unarchive_post_editor(&tab_id),
|
||||||
DeferredPostAction::Discard(tab_id) => self.discard_post_editor(&tab_id),
|
DeferredPostAction::Discard(tab_id) => self.discard_post_editor(&tab_id),
|
||||||
DeferredPostAction::ShowDelete { tab_id, name } => {
|
DeferredPostAction::ShowDelete { tab_id, name } => {
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ pub enum PostEditorMsg {
|
|||||||
RemoveCategory(String),
|
RemoveCategory(String),
|
||||||
Save,
|
Save,
|
||||||
Publish,
|
Publish,
|
||||||
|
Archive,
|
||||||
Unarchive,
|
Unarchive,
|
||||||
Discard,
|
Discard,
|
||||||
Delete,
|
Delete,
|
||||||
@@ -440,44 +441,54 @@ pub fn view<'a>(
|
|||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
|
|
||||||
let quick_actions_menu: Element<'a, Message> = container(
|
let mut quick_action_items = vec![
|
||||||
column![
|
quick_actions_busy.unwrap_or_else(|| {
|
||||||
quick_actions_busy.unwrap_or_else(|| quick_action_item(
|
quick_action_item(
|
||||||
locale,
|
locale,
|
||||||
t(locale, "editor.aiAnalyze"),
|
t(locale, "editor.aiAnalyze"),
|
||||||
PostEditorMsg::AnalyzeWithAi,
|
PostEditorMsg::AnalyzeWithAi,
|
||||||
ai_enabled && state.ai_activity.is_none()
|
ai_enabled && state.ai_activity.is_none(),
|
||||||
)),
|
)
|
||||||
quick_action_item(
|
}),
|
||||||
locale,
|
quick_action_item(
|
||||||
t(locale, "editor.suggestTaxonomy"),
|
locale,
|
||||||
PostEditorMsg::AnalyzeTaxonomy,
|
t(locale, "editor.suggestTaxonomy"),
|
||||||
ai_enabled && state.ai_activity.is_none()
|
PostEditorMsg::AnalyzeTaxonomy,
|
||||||
),
|
ai_enabled && state.ai_activity.is_none(),
|
||||||
quick_action_item(
|
),
|
||||||
locale,
|
quick_action_item(
|
||||||
t(locale, "editor.translate"),
|
locale,
|
||||||
PostEditorMsg::Translate,
|
t(locale, "editor.translate"),
|
||||||
ai_enabled && state.ai_activity.is_none()
|
PostEditorMsg::Translate,
|
||||||
),
|
ai_enabled && state.ai_activity.is_none(),
|
||||||
quick_action_item(
|
),
|
||||||
locale,
|
quick_action_item(
|
||||||
t(locale, "editor.detectLanguage"),
|
locale,
|
||||||
PostEditorMsg::DetectLanguage,
|
t(locale, "editor.detectLanguage"),
|
||||||
ai_enabled && state.ai_activity.is_none()
|
PostEditorMsg::DetectLanguage,
|
||||||
),
|
ai_enabled && state.ai_activity.is_none(),
|
||||||
quick_action_item(
|
),
|
||||||
locale,
|
quick_action_item(
|
||||||
t(locale, "editor.addGalleryImages"),
|
locale,
|
||||||
PostEditorMsg::AddGalleryImages,
|
t(locale, "editor.addGalleryImages"),
|
||||||
true
|
PostEditorMsg::AddGalleryImages,
|
||||||
),
|
true,
|
||||||
]
|
),
|
||||||
.spacing(4),
|
];
|
||||||
)
|
if !on_translation {
|
||||||
.padding(8)
|
let (label, message) = match state.status {
|
||||||
.style(status_bar::dropdown_bg)
|
PostStatus::Archived => (t(locale, "editor.unarchive"), PostEditorMsg::Unarchive),
|
||||||
.into();
|
PostStatus::Draft | PostStatus::Published => {
|
||||||
|
(t(locale, "editor.archive"), PostEditorMsg::Archive)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
quick_action_items.push(quick_action_item(locale, label, message, true));
|
||||||
|
}
|
||||||
|
let quick_actions_menu: Element<'a, Message> =
|
||||||
|
container(iced::widget::Column::with_children(quick_action_items).spacing(4))
|
||||||
|
.padding(8)
|
||||||
|
.style(status_bar::dropdown_bg)
|
||||||
|
.into();
|
||||||
let quick_actions: Element<'a, Message> = popover::popover(
|
let quick_actions: Element<'a, Message> = popover::popover(
|
||||||
quick_actions_button,
|
quick_actions_button,
|
||||||
quick_actions_menu,
|
quick_actions_menu,
|
||||||
@@ -512,19 +523,6 @@ pub fn view<'a>(
|
|||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !on_translation && state.status == PostStatus::Archived {
|
|
||||||
header_action_items.push(
|
|
||||||
button(
|
|
||||||
text(t(locale, "editor.unarchive"))
|
|
||||||
.size(13)
|
|
||||||
.shaping(Shaping::Advanced),
|
|
||||||
)
|
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::Unarchive))
|
|
||||||
.style(inputs::secondary_button)
|
|
||||||
.padding([6, 16])
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if !on_translation && state.status == PostStatus::Draft && state.published_at.is_some() {
|
if !on_translation && state.status == PostStatus::Draft && state.published_at.is_some() {
|
||||||
header_action_items.push(
|
header_action_items.push(
|
||||||
button(
|
button(
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ editor-translate = Übersetzen
|
|||||||
editor-templateSlug = Vorlage
|
editor-templateSlug = Vorlage
|
||||||
editor-doNotTranslate = Nicht übersetzen
|
editor-doNotTranslate = Nicht übersetzen
|
||||||
editor-publish = Veröffentlichen
|
editor-publish = Veröffentlichen
|
||||||
|
editor-archive = Archivieren
|
||||||
|
editor-archived = Ins Archiv verschoben.
|
||||||
editor-unarchive = Wiederherstellen
|
editor-unarchive = Wiederherstellen
|
||||||
editor-unarchived = Als Entwurf wiederhergestellt.
|
editor-unarchived = Als Entwurf wiederhergestellt.
|
||||||
editor-statusDraft = Entwurf
|
editor-statusDraft = Entwurf
|
||||||
|
|||||||
@@ -404,6 +404,8 @@ editor-translate = Translate
|
|||||||
editor-templateSlug = Template
|
editor-templateSlug = Template
|
||||||
editor-doNotTranslate = Do Not Translate
|
editor-doNotTranslate = Do Not Translate
|
||||||
editor-publish = Publish
|
editor-publish = Publish
|
||||||
|
editor-archive = Archive
|
||||||
|
editor-archived = Moved to archive.
|
||||||
editor-unarchive = Unarchive
|
editor-unarchive = Unarchive
|
||||||
editor-unarchived = Restored to draft.
|
editor-unarchived = Restored to draft.
|
||||||
editor-statusDraft = Draft
|
editor-statusDraft = Draft
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ editor-translate = Traducir
|
|||||||
editor-templateSlug = Plantilla
|
editor-templateSlug = Plantilla
|
||||||
editor-doNotTranslate = No traducir
|
editor-doNotTranslate = No traducir
|
||||||
editor-publish = Publicar
|
editor-publish = Publicar
|
||||||
|
editor-archive = Archivar
|
||||||
|
editor-archived = Movido al archivo.
|
||||||
editor-unarchive = Desarchivar
|
editor-unarchive = Desarchivar
|
||||||
editor-unarchived = Restaurado como borrador.
|
editor-unarchived = Restaurado como borrador.
|
||||||
editor-statusDraft = Borrador
|
editor-statusDraft = Borrador
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ editor-translate = Traduire
|
|||||||
editor-templateSlug = Modèle
|
editor-templateSlug = Modèle
|
||||||
editor-doNotTranslate = Ne pas traduire
|
editor-doNotTranslate = Ne pas traduire
|
||||||
editor-publish = Publier
|
editor-publish = Publier
|
||||||
|
editor-archive = Archiver
|
||||||
|
editor-archived = Déplacé dans les archives.
|
||||||
editor-unarchive = Désarchiver
|
editor-unarchive = Désarchiver
|
||||||
editor-unarchived = Restauré comme brouillon.
|
editor-unarchived = Restauré comme brouillon.
|
||||||
editor-statusDraft = Brouillon
|
editor-statusDraft = Brouillon
|
||||||
|
|||||||
@@ -419,6 +419,8 @@ editor-translate = Traduci
|
|||||||
editor-templateSlug = Modello
|
editor-templateSlug = Modello
|
||||||
editor-doNotTranslate = Non tradurre
|
editor-doNotTranslate = Non tradurre
|
||||||
editor-publish = Pubblica
|
editor-publish = Pubblica
|
||||||
|
editor-archive = Archivia
|
||||||
|
editor-archived = Spostato in archivio.
|
||||||
editor-unarchive = Ripristina
|
editor-unarchive = Ripristina
|
||||||
editor-unarchived = Ripristinato come bozza.
|
editor-unarchived = Ripristinato come bozza.
|
||||||
editor-statusDraft = Bozza
|
editor-statusDraft = Bozza
|
||||||
|
|||||||
@@ -237,6 +237,9 @@ rule ArchivePost {
|
|||||||
when: ArchivePostRequested(post)
|
when: ArchivePostRequested(post)
|
||||||
requires: post.status = draft or post.status = published
|
requires: post.status = draft or post.status = published
|
||||||
ensures: post.status = archived
|
ensures: post.status = archived
|
||||||
|
-- Archiving changes only status and updatedAt. A published post keeps its
|
||||||
|
-- body solely in the existing file: content remains null and the file is
|
||||||
|
-- not rewritten.
|
||||||
}
|
}
|
||||||
|
|
||||||
rule UnarchivePost {
|
rule UnarchivePost {
|
||||||
|
|||||||
Reference in New Issue
Block a user