feat: remove the post duplication feature (closes #10)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,7 @@ Open:
|
|||||||
Available:
|
Available:
|
||||||
|
|
||||||
- Dashboard and editors for posts, translations, media, tags, templates, scripts, and settings.
|
- Dashboard and editors for posts, translations, media, tags, templates, scripts, and settings.
|
||||||
- Post create, edit, duplicate, publish, unpublish, discard, and delete flows.
|
- Post create, edit, publish, unpublish, discard, and delete flows.
|
||||||
- Media import, replacement, metadata editing, translations, thumbnails, filters, and post assignment.
|
- Media import, replacement, metadata editing, translations, thumbnails, filters, and post assignment.
|
||||||
- Template and Lua script creation, editing, validation, publication, and deletion.
|
- Template and Lua script creation, editing, validation, publication, and deletion.
|
||||||
- Rope-based editing with syntax highlighting, selection, clipboard, undo/redo, word/line/page movement, line numbers, soft wrapping, mouse selection, and committed IME input.
|
- Rope-based editing with syntax highlighting, selection, clipboard, undo/redo, word/line/page movement, line numbers, soft wrapping, mouse selection, and committed IME input.
|
||||||
|
|||||||
@@ -457,60 +457,6 @@ pub fn discard_post_draft(conn: &Connection, data_dir: &Path, post_id: &str) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new draft post by duplicating an existing post.
|
|
||||||
pub fn duplicate_post(conn: &Connection, data_dir: &Path, post_id: &str) -> EngineResult<Post> {
|
|
||||||
let source = qp::get_post_by_id(conn, post_id)?;
|
|
||||||
let body = if let Some(ref content) = source.content {
|
|
||||||
content.clone()
|
|
||||||
} else if !source.file_path.is_empty() {
|
|
||||||
let abs_path = data_dir.join(&source.file_path);
|
|
||||||
if abs_path.exists() {
|
|
||||||
let raw = fs::read_to_string(&abs_path)?;
|
|
||||||
let (_fm, body) = read_post_file(&raw).map_err(EngineError::Parse)?;
|
|
||||||
body
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let duplicate_title = if source.title.is_empty() {
|
|
||||||
"Untitled Copy".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{} Copy", source.title)
|
|
||||||
};
|
|
||||||
|
|
||||||
let duplicated = create_post(
|
|
||||||
conn,
|
|
||||||
data_dir,
|
|
||||||
&source.project_id,
|
|
||||||
&duplicate_title,
|
|
||||||
Some(&body),
|
|
||||||
source.tags.clone(),
|
|
||||||
source.categories.clone(),
|
|
||||||
source.author.as_deref(),
|
|
||||||
source.language.as_deref(),
|
|
||||||
source.template_slug.as_deref(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
update_post(
|
|
||||||
conn,
|
|
||||||
data_dir,
|
|
||||||
&duplicated.id,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(source.excerpt.as_deref()),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(source.do_not_translate),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a post and all related data.
|
/// Delete a post and all related data.
|
||||||
pub fn delete_post(conn: &Connection, data_dir: &Path, post_id: &str) -> EngineResult<()> {
|
pub fn delete_post(conn: &Connection, data_dir: &Path, post_id: &str) -> EngineResult<()> {
|
||||||
let post = qp::get_post_by_id(conn, post_id)?;
|
let post = qp::get_post_by_id(conn, post_id)?;
|
||||||
@@ -1540,54 +1486,6 @@ mod tests {
|
|||||||
assert_eq!(unchanged.content.as_deref(), Some("draft body"));
|
assert_eq!(unchanged.content.as_deref(), Some("draft body"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn duplicate_post_creates_new_draft_copy() {
|
|
||||||
let (db, dir) = setup();
|
|
||||||
let post = create_post(
|
|
||||||
db.conn(),
|
|
||||||
dir.path(),
|
|
||||||
"p1",
|
|
||||||
"Original",
|
|
||||||
Some("body text"),
|
|
||||||
vec!["rust".into()],
|
|
||||||
vec!["guide".into()],
|
|
||||||
Some("Alice"),
|
|
||||||
Some("en"),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let source = update_post(
|
|
||||||
db.conn(),
|
|
||||||
dir.path(),
|
|
||||||
&post.id,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(Some("excerpt")),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(true),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let duplicate = duplicate_post(db.conn(), dir.path(), &post.id).unwrap();
|
|
||||||
|
|
||||||
assert_ne!(duplicate.id, source.id);
|
|
||||||
assert_eq!(duplicate.status, PostStatus::Draft);
|
|
||||||
assert_eq!(duplicate.title, "Original Copy");
|
|
||||||
assert_eq!(duplicate.content.as_deref(), Some("body text"));
|
|
||||||
assert_eq!(duplicate.tags, source.tags);
|
|
||||||
assert_eq!(duplicate.categories, source.categories);
|
|
||||||
assert_eq!(duplicate.author, source.author);
|
|
||||||
assert_eq!(duplicate.language, source.language);
|
|
||||||
assert_eq!(duplicate.excerpt, source.excerpt);
|
|
||||||
assert!(duplicate.do_not_translate);
|
|
||||||
assert!(duplicate.slug.starts_with("original-copy"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn publish_preserves_published_at_on_republish() {
|
fn publish_preserves_published_at_on_republish() {
|
||||||
let (db, dir) = setup();
|
let (db, dir) = setup();
|
||||||
|
|||||||
@@ -3744,36 +3744,6 @@ impl BdsApp {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duplicate_post_editor(&mut self, post_id: &str) -> Task<Message> {
|
|
||||||
let Some(db) = &self.db else {
|
|
||||||
return Task::none();
|
|
||||||
};
|
|
||||||
let Some(data_dir) = &self.data_dir else {
|
|
||||||
return Task::none();
|
|
||||||
};
|
|
||||||
match engine::post::duplicate_post(db.conn(), data_dir, post_id) {
|
|
||||||
Ok(post) => {
|
|
||||||
let tab = Tab {
|
|
||||||
id: post.id.clone(),
|
|
||||||
title: post.title.clone(),
|
|
||||||
tab_type: TabType::Post,
|
|
||||||
is_transient: false,
|
|
||||||
is_dirty: false,
|
|
||||||
};
|
|
||||||
let idx = tabs::open_tab(&mut self.tabs, tab);
|
|
||||||
self.active_tab = self.tabs.get(idx).map(|tab| tab.id.clone());
|
|
||||||
if let Some(tab) = self.tabs.get(idx).cloned() {
|
|
||||||
self.load_editor_for_tab(&tab);
|
|
||||||
}
|
|
||||||
self.enforce_panel_tab_fallback();
|
|
||||||
self.sync_menu_state();
|
|
||||||
self.notify(ToastLevel::Success, &t(self.ui_locale, "editor.saved"));
|
|
||||||
}
|
|
||||||
Err(e) => self.notify_operation_failed("editor.duplicate", e),
|
|
||||||
}
|
|
||||||
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();
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ impl BdsApp {
|
|||||||
},
|
},
|
||||||
Save(String),
|
Save(String),
|
||||||
Publish(String),
|
Publish(String),
|
||||||
Duplicate(String),
|
|
||||||
Discard(String),
|
Discard(String),
|
||||||
ShowDelete {
|
ShowDelete {
|
||||||
tab_id: String,
|
tab_id: String,
|
||||||
@@ -169,9 +168,6 @@ impl BdsApp {
|
|||||||
PostEditorMsg::Publish => {
|
PostEditorMsg::Publish => {
|
||||||
deferred = DeferredPostAction::Publish(tab_id.clone());
|
deferred = DeferredPostAction::Publish(tab_id.clone());
|
||||||
}
|
}
|
||||||
PostEditorMsg::Duplicate => {
|
|
||||||
deferred = DeferredPostAction::Duplicate(tab_id.clone());
|
|
||||||
}
|
|
||||||
PostEditorMsg::Discard => {
|
PostEditorMsg::Discard => {
|
||||||
deferred = DeferredPostAction::Discard(tab_id.clone());
|
deferred = DeferredPostAction::Discard(tab_id.clone());
|
||||||
}
|
}
|
||||||
@@ -280,7 +276,6 @@ 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),
|
DeferredPostAction::Save(tab_id) => self.save_post_editor(&tab_id),
|
||||||
DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id),
|
DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id),
|
||||||
DeferredPostAction::Duplicate(tab_id) => self.duplicate_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 } => {
|
||||||
Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete {
|
Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete {
|
||||||
|
|||||||
@@ -347,7 +347,6 @@ pub enum PostEditorMsg {
|
|||||||
RemoveCategory(String),
|
RemoveCategory(String),
|
||||||
Save,
|
Save,
|
||||||
Publish,
|
Publish,
|
||||||
Duplicate,
|
|
||||||
Discard,
|
Discard,
|
||||||
Delete,
|
Delete,
|
||||||
InsertLink,
|
InsertLink,
|
||||||
@@ -412,19 +411,6 @@ pub fn view<'a>(
|
|||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
];
|
];
|
||||||
if !on_translation {
|
|
||||||
header_action_items.push(
|
|
||||||
button(
|
|
||||||
text(t(locale, "editor.duplicate"))
|
|
||||||
.size(13)
|
|
||||||
.shaping(Shaping::Advanced),
|
|
||||||
)
|
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::Duplicate))
|
|
||||||
.padding([6, 16])
|
|
||||||
.style(inputs::secondary_button)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if state.status == PostStatus::Draft {
|
if state.status == PostStatus::Draft {
|
||||||
header_action_items.push(
|
header_action_items.push(
|
||||||
button(
|
button(
|
||||||
|
|||||||
@@ -267,7 +267,6 @@ editor-templateSlug = Vorlage
|
|||||||
editor-doNotTranslate = Nicht übersetzen
|
editor-doNotTranslate = Nicht übersetzen
|
||||||
editor-publish = Veröffentlichen
|
editor-publish = Veröffentlichen
|
||||||
editor-discard = Verwerfen
|
editor-discard = Verwerfen
|
||||||
editor-duplicate = Duplizieren
|
|
||||||
editor-validate = Validieren
|
editor-validate = Validieren
|
||||||
editor-run = Ausführen
|
editor-run = Ausführen
|
||||||
editor-checkSyntax = Syntax prüfen
|
editor-checkSyntax = Syntax prüfen
|
||||||
|
|||||||
@@ -264,7 +264,6 @@ editor-templateSlug = Template
|
|||||||
editor-doNotTranslate = Do Not Translate
|
editor-doNotTranslate = Do Not Translate
|
||||||
editor-publish = Publish
|
editor-publish = Publish
|
||||||
editor-discard = Discard
|
editor-discard = Discard
|
||||||
editor-duplicate = Duplicate
|
|
||||||
editor-validate = Validate
|
editor-validate = Validate
|
||||||
editor-run = Run
|
editor-run = Run
|
||||||
editor-checkSyntax = Check Syntax
|
editor-checkSyntax = Check Syntax
|
||||||
|
|||||||
@@ -267,7 +267,6 @@ editor-templateSlug = Plantilla
|
|||||||
editor-doNotTranslate = No traducir
|
editor-doNotTranslate = No traducir
|
||||||
editor-publish = Publicar
|
editor-publish = Publicar
|
||||||
editor-discard = Descartar
|
editor-discard = Descartar
|
||||||
editor-duplicate = Duplicar
|
|
||||||
editor-validate = Validar
|
editor-validate = Validar
|
||||||
editor-run = Ejecutar
|
editor-run = Ejecutar
|
||||||
editor-checkSyntax = Verificar sintaxis
|
editor-checkSyntax = Verificar sintaxis
|
||||||
|
|||||||
@@ -267,7 +267,6 @@ editor-templateSlug = Modèle
|
|||||||
editor-doNotTranslate = Ne pas traduire
|
editor-doNotTranslate = Ne pas traduire
|
||||||
editor-publish = Publier
|
editor-publish = Publier
|
||||||
editor-discard = Annuler les modifications
|
editor-discard = Annuler les modifications
|
||||||
editor-duplicate = Dupliquer
|
|
||||||
editor-validate = Valider
|
editor-validate = Valider
|
||||||
editor-run = Exécuter
|
editor-run = Exécuter
|
||||||
editor-checkSyntax = Vérifier la syntaxe
|
editor-checkSyntax = Vérifier la syntaxe
|
||||||
|
|||||||
@@ -267,7 +267,6 @@ editor-templateSlug = Modello
|
|||||||
editor-doNotTranslate = Non tradurre
|
editor-doNotTranslate = Non tradurre
|
||||||
editor-publish = Pubblica
|
editor-publish = Pubblica
|
||||||
editor-discard = Scarta
|
editor-discard = Scarta
|
||||||
editor-duplicate = Duplica
|
|
||||||
editor-validate = Valida
|
editor-validate = Valida
|
||||||
editor-run = Esegui
|
editor-run = Esegui
|
||||||
editor-checkSyntax = Controlla sintassi
|
editor-checkSyntax = Controlla sintassi
|
||||||
|
|||||||
Reference in New Issue
Block a user