diff --git a/src/Integrations/WordPress/Content/ContentCreateAbility.php b/src/Integrations/WordPress/Content/ContentCreateAbility.php index feb590d..e4a14e7 100644 --- a/src/Integrations/WordPress/Content/ContentCreateAbility.php +++ b/src/Integrations/WordPress/Content/ContentCreateAbility.php @@ -6,11 +6,10 @@ namespace LIT\MCP\Integrations\WordPress\Content; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; -use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\Abilities\WriteFailed; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use Throwable; @@ -136,29 +135,15 @@ final class ContentCreateAbility implements Ability private function result(int $postId, string $postType, array $input, array $warnings): array { // The draft is saved – failures from here on are logged and reported as warnings only. - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Speichern auf einen Fehler gestoßen; der Entwurf ist gespeichert.', - $warnings - ); - - $post = get_post($postId); + $change = (new PostChangeResult($this->name(), $this->log, $postId, 'der Entwurf', $warnings)) + ->afterSave($input); + $post = $change->reread(); $title = $post instanceof WP_Post ? (string) $post->post_title : (string) $input['title']; - $summary = sprintf('Entwurf "%s" angelegt', $title); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), $postType, (string) $postId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; der Entwurf ist gespeichert.', - $warnings - ); + $change->log($postType, sprintf('Entwurf "%s" angelegt', $title)); - return ToolResult::ok(sprintf('"%s" als Entwurf angelegt (ID %d).', $title, $postId)) + return $change->result(sprintf('"%s" als Entwurf angelegt (ID %d).', $title, $postId), $post) ->changed((string) $postId, $postType, 'created', $title) - ->rev($post instanceof WP_Post ? Rev::ofPost($post) : null) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($postId), (string) get_edit_post_link($postId, 'raw')) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/Content/ContentUpdateAbility.php b/src/Integrations/WordPress/Content/ContentUpdateAbility.php index 3d439c2..b8a32d7 100644 --- a/src/Integrations/WordPress/Content/ContentUpdateAbility.php +++ b/src/Integrations/WordPress/Content/ContentUpdateAbility.php @@ -6,11 +6,11 @@ namespace LIT\MCP\Integrations\WordPress\Content; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\Abilities\WriteFailed; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\RevisionPoint; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; @@ -178,31 +178,17 @@ final class ContentUpdateAbility implements Ability $postType = (string) $post->post_type; // The change is saved – failures from here on are logged and reported as warnings only. - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Speichern auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); - - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; + $change = (new PostChangeResult($this->name(), $this->log, $postId, 'die Änderung', $warnings)) + ->afterSave($input); + $updated = $change->reread($post) ?? $post; $changedKeys = array_keys(array_intersect_key($input, array_flip(self::CHANGE_KEYS))); $summary = sprintf('"%s" geändert: %s', $updated->post_title, implode(', ', $changedKeys)); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), $postType, (string) $postId, $summary, $revisionId), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); + $change->log($postType, $summary, $revisionId); - return ToolResult::ok(sprintf('%s (Status unverändert: %s).', $summary, $updated->post_status)) + return $change->result(sprintf('%s (Status unverändert: %s).', $summary, $updated->post_status), $updated) ->changed((string) $postId, $postType, 'updated', (string) $updated->post_title) - ->rev(Rev::ofPost($updated)) ->revisionId($revisionId) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($updated), (string) get_edit_post_link($postId, 'raw')) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/PostChangeResult.php b/src/Integrations/WordPress/PostChangeResult.php new file mode 100644 index 0000000..b4e317a --- /dev/null +++ b/src/Integrations/WordPress/PostChangeResult.php @@ -0,0 +1,89 @@ + */ + private array $warnings; + + private ?int $logId = null; + + /** + * @param string $saved What is saved, for the warnings, e.g. "die Änderung", "der Entwurf". + * @param list $warnings Warnings collected before the write. + */ + public function __construct( + private string $ability, + private ChangeLog $log, + private int $postId, + private string $saved = 'die Änderung', + array $warnings = [] + ) { + $this->warnings = $warnings; + } + + /** + * @param array $input + * @param string $after The step just done, e.g. "Speichern", "Veröffentlichen". + */ + public function afterSave(array $input, string $after = 'Speichern'): self + { + FollowUp::run( + fn () => do_action('lit_mcp_after_save', $this->postId, $this->ability, $input), + $this->ability . ' lit_mcp_after_save', + sprintf( + 'Eine Erweiterung ist nach dem %s auf einen Fehler gestoßen; %s ist gespeichert.', + $after, + $this->saved + ), + $this->warnings + ); + + return $this; + } + + /** The saved post as stored now; $fallback when it cannot be read. */ + public function reread(?WP_Post $fallback = null): ?WP_Post + { + $post = get_post($this->postId); + + return $post instanceof WP_Post ? $post : $fallback; + } + + public function log(string $objectType, string $summary, ?int $revisionId = null): self + { + $logId = FollowUp::run( + fn () => $this->log->record($this->ability, $objectType, (string) $this->postId, $summary, $revisionId), + $this->ability . ' change log', + sprintf('Änderungsprotokoll konnte nicht geschrieben werden; %s ist gespeichert.', $this->saved), + $this->warnings + ); + $this->logId = is_int($logId) ? $logId : null; + + return $this; + } + + /** Result with the rev of $post, the log id and all warnings; callers add changed, urls, … */ + public function result(string $summary, ?WP_Post $post): ToolResult + { + return ToolResult::ok($summary) + ->rev($post instanceof WP_Post ? Rev::ofPost($post) : null) + ->logId($this->logId) + ->warnings($this->warnings); + } +} diff --git a/src/Integrations/WordPress/Revisions/RevisionRestoreAbility.php b/src/Integrations/WordPress/Revisions/RevisionRestoreAbility.php index c6b9c88..32014a9 100644 --- a/src/Integrations/WordPress/Revisions/RevisionRestoreAbility.php +++ b/src/Integrations/WordPress/Revisions/RevisionRestoreAbility.php @@ -6,10 +6,10 @@ namespace LIT\MCP\Integrations\WordPress\Revisions; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\RevisionPoint; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; @@ -124,32 +124,16 @@ final class RevisionRestoreAbility implements Ability } // The restore is saved – failures from here on are logged and reported as warnings only. - $warnings = []; - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Wiederherstellen auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); - - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; + $change = (new PostChangeResult($this->name(), $this->log, $postId))->afterSave($input, 'Wiederherstellen'); + $updated = $change->reread($post) ?? $post; $summary = sprintf('"%s" auf Stand vom %s zurückgesetzt', $updated->post_title, $revision->post_date); $postType = (string) $post->post_type; - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), $postType, (string) $postId, $summary, $backupId), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); + $change->log($postType, $summary, $backupId); - return ToolResult::ok($summary . sprintf(' (Status unverändert: %s).', $updated->post_status)) + return $change->result($summary . sprintf(' (Status unverändert: %s).', $updated->post_status), $updated) ->changed((string) $postId, $postType, 'restored', (string) $updated->post_title) - ->rev(Rev::ofPost($updated)) ->revisionId($backupId) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($updated), (string) get_edit_post_link($postId, 'raw')) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostDuplicateAbility.php b/src/Integrations/WordPress/Status/PostDuplicateAbility.php index 704cadf..2a8406f 100644 --- a/src/Integrations/WordPress/Status/PostDuplicateAbility.php +++ b/src/Integrations/WordPress/Status/PostDuplicateAbility.php @@ -5,10 +5,9 @@ declare(strict_types=1); namespace LIT\MCP\Integrations\WordPress\Status; use LIT\MCP\Abilities\Ability; -use LIT\MCP\Abilities\FollowUp; -use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use WP_Error; @@ -88,31 +87,17 @@ final class PostDuplicateAbility implements Ability $copyId = $this->cloner->duplicate($original); // The copy is saved – failures from here on are logged and reported as warnings only. - $warnings = []; - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $copyId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Anlegen der Kopie auf einen Fehler gestoßen; die Kopie ist gespeichert.', - $warnings - ); - - $copy = get_post($copyId); + $change = (new PostChangeResult($this->name(), $this->log, $copyId, 'die Kopie')) + ->afterSave($input, 'Anlegen der Kopie'); + $copy = $change->reread(); $copyTitle = $copy instanceof WP_Post ? (string) $copy->post_title : ''; $postType = (string) $original->post_type; $summary = sprintf('Kopie von "%s" (ID %d) angelegt', $original->post_title, $original->ID); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), $postType, (string) $copyId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Kopie ist gespeichert.', - $warnings - ); + $change->log($postType, $summary); - return ToolResult::ok(sprintf('%s: Entwurf ID %d.', $summary, $copyId)) + return $change->result(sprintf('%s: Entwurf ID %d.', $summary, $copyId), $copy) ->changed((string) $copyId, $postType, 'created', $copyTitle) - ->rev($copy instanceof WP_Post ? Rev::ofPost($copy) : null) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($copyId), (string) get_edit_post_link($copyId, 'raw')) - ->warnings($warnings) ->with('original_id', (int) $original->ID) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostPublishAbility.php b/src/Integrations/WordPress/Status/PostPublishAbility.php index 67b0c46..3c31632 100644 --- a/src/Integrations/WordPress/Status/PostPublishAbility.php +++ b/src/Integrations/WordPress/Status/PostPublishAbility.php @@ -6,10 +6,10 @@ namespace LIT\MCP\Integrations\WordPress\Status; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use WP_Error; @@ -114,31 +114,12 @@ final class PostPublishAbility implements Ability } // The status change is saved – failures from here on are logged and reported as warnings only. - $warnings = []; - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Veröffentlichen auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); + $change = (new PostChangeResult($this->name(), $this->log, $postId))->afterSave($input, 'Veröffentlichen'); + $change->log((string) $post->post_type, sprintf('"%s" veröffentlicht', $title)); - $summary = sprintf('"%s" veröffentlicht', $title); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), (string) $post->post_type, (string) $postId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); - - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; - - return ToolResult::ok(sprintf('"%s" ist jetzt veröffentlicht.', $title)) + return $change->result(sprintf('"%s" ist jetzt veröffentlicht.', $title), $change->reread($post)) ->changed((string) $postId, (string) $post->post_type, 'published', $title) - ->rev(Rev::ofPost($updated)) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_permalink($postId), $editUrl) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostReplaceWithCopyAbility.php b/src/Integrations/WordPress/Status/PostReplaceWithCopyAbility.php index 711e1f2..31675db 100644 --- a/src/Integrations/WordPress/Status/PostReplaceWithCopyAbility.php +++ b/src/Integrations/WordPress/Status/PostReplaceWithCopyAbility.php @@ -11,6 +11,7 @@ use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\Abilities\WriteFailed; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\RevisionPoint; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; @@ -221,24 +222,11 @@ final class PostReplaceWithCopyAbility implements Ability $warnings[] = $trashWarning; } - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $originalId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Speichern auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); - + $change = (new PostChangeResult($this->name(), $this->log, $originalId, 'die Änderung', $warnings)) + ->afterSave($input); $title = PostCloner::withoutSuffix((string) $copy->post_title); $summary = sprintf('Kopie ID %d in "%s" (ID %d) übernommen', $copyId, $title, $originalId); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), $postType, (string) $originalId, $summary, $revisionId), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); - - $updated = get_post($originalId); - $updated = $updated instanceof WP_Post ? $updated : $original; + $change->log($postType, $summary, $revisionId); $statusNote = sprintf( '; Status bleibt %s, %s, der vorherige Stand liegt als Sicherung ID %d im Papierkorb.', $original->post_status, @@ -246,18 +234,15 @@ final class PostReplaceWithCopyAbility implements Ability $backupId ); - $result = ToolResult::ok($summary . $statusNote) + $result = $change->result($summary . $statusNote, $change->reread($original)) ->changed((string) $originalId, $postType, 'replaced', $title); if ($trashed) { $result->changed((string) $copyId, $postType, 'trashed', (string) $copy->post_title); } return $result - ->rev(Rev::ofPost($updated)) ->revisionId($revisionId) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_permalink($originalId), (string) get_edit_post_link($originalId, 'raw')) - ->warnings($warnings) ->with('backup_id', $backupId) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostScheduleAbility.php b/src/Integrations/WordPress/Status/PostScheduleAbility.php index d6ce674..986a82c 100644 --- a/src/Integrations/WordPress/Status/PostScheduleAbility.php +++ b/src/Integrations/WordPress/Status/PostScheduleAbility.php @@ -8,10 +8,9 @@ use DateTimeImmutable; use DateTimeZone; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; -use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use WP_Error; @@ -128,33 +127,16 @@ final class PostScheduleAbility implements Ability } // The status change is saved – failures from here on are logged and reported as warnings only. - $warnings = []; - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Planen auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); - + $change = (new PostChangeResult($this->name(), $this->log, $postId))->afterSave($input, 'Planen'); $title = (string) $post->post_title; $summary = sprintf('"%s" geplant für %s', $title, $raw) . ($post->post_status === 'publish' ? ' (war veröffentlicht und ist bis dahin offline)' : ''); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), (string) $post->post_type, (string) $postId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); + $change->log((string) $post->post_type, $summary); + $updated = $change->reread($post) ?? $post; - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; - - return ToolResult::ok($summary . '.') + return $change->result($summary . '.', $updated) ->changed((string) $postId, (string) $post->post_type, 'scheduled', $title) - ->rev(Rev::ofPost($updated)) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($updated), (string) get_edit_post_link($postId, 'raw')) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostTrashAbility.php b/src/Integrations/WordPress/Status/PostTrashAbility.php index 4c91efb..5b0da32 100644 --- a/src/Integrations/WordPress/Status/PostTrashAbility.php +++ b/src/Integrations/WordPress/Status/PostTrashAbility.php @@ -6,10 +6,10 @@ namespace LIT\MCP\Integrations\WordPress\Status; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use WP_Error; @@ -121,23 +121,13 @@ final class PostTrashAbility implements Ability } // The change is saved – a failure of the change log from here on is a warning only. - $summary = sprintf('"%s" in den Papierkorb gelegt', $title); - $warnings = []; - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), (string) $post->post_type, (string) $postId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); + $change = (new PostChangeResult($this->name(), $this->log, $postId)) + ->log((string) $post->post_type, sprintf('"%s" in den Papierkorb gelegt', $title)); - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; + $summary = sprintf('"%s" liegt im Papierkorb und ist dort wiederherstellbar.', $title); - return ToolResult::ok(sprintf('"%s" liegt im Papierkorb und ist dort wiederherstellbar.', $title)) + return $change->result($summary, $change->reread($post)) ->changed((string) $postId, (string) $post->post_type, 'trashed', $title) - ->rev(Rev::ofPost($updated)) - ->logId(is_int($logId) ? $logId : null) - ->warnings($warnings) ->toArray(); } diff --git a/src/Integrations/WordPress/Status/PostUnpublishAbility.php b/src/Integrations/WordPress/Status/PostUnpublishAbility.php index d82ffa9..b16141e 100644 --- a/src/Integrations/WordPress/Status/PostUnpublishAbility.php +++ b/src/Integrations/WordPress/Status/PostUnpublishAbility.php @@ -6,14 +6,13 @@ namespace LIT\MCP\Integrations\WordPress\Status; use LIT\MCP\Abilities\Ability; use LIT\MCP\Abilities\Errors; -use LIT\MCP\Abilities\FollowUp; use LIT\MCP\Abilities\Rev; use LIT\MCP\Abilities\ToolResult; use LIT\MCP\ChangeLog\ChangeLog; +use LIT\MCP\Integrations\WordPress\PostChangeResult; use LIT\MCP\Integrations\WordPress\PostGuard; use LIT\MCP\Integrations\WordPress\Schema\ContentSchema; use WP_Error; -use WP_Post; /** * lit/post-unpublish: withdraws a published, scheduled or private entry back to draft. @@ -98,31 +97,14 @@ final class PostUnpublishAbility implements Ability } // The status change is saved – failures from here on are logged and reported as warnings only. - $warnings = []; - FollowUp::run( - fn () => do_action('lit_mcp_after_save', $postId, $this->name(), $input), - $this->name() . ' lit_mcp_after_save', - 'Eine Erweiterung ist nach dem Zurückziehen auf einen Fehler gestoßen; die Änderung ist gespeichert.', - $warnings - ); - + $change = (new PostChangeResult($this->name(), $this->log, $postId))->afterSave($input, 'Zurückziehen'); $summary = sprintf('"%s" zurückgezogen (vorher %s, jetzt Entwurf)', $title, $post->post_status); - $logId = FollowUp::run( - fn () => $this->log->record($this->name(), (string) $post->post_type, (string) $postId, $summary), - $this->name() . ' change log', - 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', - $warnings - ); + $change->log((string) $post->post_type, $summary); + $updated = $change->reread($post) ?? $post; - $updated = get_post($postId); - $updated = $updated instanceof WP_Post ? $updated : $post; - - return ToolResult::ok($summary . '.') + return $change->result($summary . '.', $updated) ->changed((string) $postId, (string) $post->post_type, 'unpublished', $title) - ->rev(Rev::ofPost($updated)) - ->logId(is_int($logId) ? $logId : null) ->urls((string) get_preview_post_link($updated), $editUrl) - ->warnings($warnings) ->toArray(); } } diff --git a/tests/Unit/Integrations/WordPress/Content/ContentCreateAbilityTest.php b/tests/Unit/Integrations/WordPress/Content/ContentCreateAbilityTest.php index c104a55..e036260 100644 --- a/tests/Unit/Integrations/WordPress/Content/ContentCreateAbilityTest.php +++ b/tests/Unit/Integrations/WordPress/Content/ContentCreateAbilityTest.php @@ -96,7 +96,7 @@ final class ContentCreateAbilityTest extends TestCase Actions\expectDone('lit_mcp_after_save')->once()->with(42, 'lit/content-create', $input); $post = $this->stubResultLinks(); $log = Mockery::mock(ChangeLog::class); - $log->expects('record')->with('lit/content-create', 'lit_person', '42', 'Entwurf "Anna" angelegt')->andReturn(7); + $log->expects('record')->with('lit/content-create', 'lit_person', '42', 'Entwurf "Anna" angelegt', null)->andReturn(7); $result = (new ContentCreateAbility($schema, $writer, $log))->execute($input); diff --git a/tests/Unit/Integrations/WordPress/PostChangeResultTest.php b/tests/Unit/Integrations/WordPress/PostChangeResultTest.php new file mode 100644 index 0000000..f7463e1 --- /dev/null +++ b/tests/Unit/Integrations/WordPress/PostChangeResultTest.php @@ -0,0 +1,91 @@ +errorLog = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log'); + $this->previousLog = ini_set('error_log', $this->errorLog); + } + + protected function tearDown(): void + { + ini_set('error_log', (string) $this->previousLog); + unlink($this->errorLog); + parent::tearDown(); + } + + public function testRunsTheHookLogsAndBuildsTheResult(): void + { + $post = new WP_Post(['ID' => 5, 'post_title' => 'Team']); + Actions\expectDone('lit_mcp_after_save')->once()->with(5, 'lit/content-update', ['post_id' => 5]); + Functions\when('get_post')->justReturn($post); + $log = Mockery::mock(ChangeLog::class); + $log->expects('record')->with('lit/content-update', 'page', '5', 'geändert', 811)->andReturn(9); + + $change = new PostChangeResult('lit/content-update', $log, 5, 'die Änderung', ['vorher']); + $change->afterSave(['post_id' => 5]); + $updated = $change->reread(); + $change->log('page', 'geändert', 811); + $result = $change->result('Fertig.', $updated)->toArray(); + + self::assertSame($post, $updated); + self::assertSame('Fertig.', $result['summary']); + self::assertSame(Rev::ofPost($post), $result['rev']); + self::assertSame(9, $result['log_id']); + self::assertSame(['vorher'], $result['warnings']); + } + + public function testFailuresAfterTheWriteBecomeWarnings(): void + { + Actions\expectDone('lit_mcp_after_save')->once()->whenHappen(static function (): void { + throw new RuntimeException('listener kaputt'); + }); + Functions\when('get_post')->justReturn(null); + $log = Mockery::mock(ChangeLog::class); + $log->expects('record')->andThrow(new RuntimeException('log kaputt')); + $fallback = new WP_Post(['ID' => 5]); + + $change = new PostChangeResult('lit/post-publish', $log, 5); + $change->afterSave([], 'Veröffentlichen'); + $updated = $change->reread($fallback); + $change->log('page', 'x'); + $result = $change->result('ok', $updated)->toArray(); + + self::assertSame($fallback, $updated); + self::assertNull($result['log_id']); + self::assertSame([ + 'Eine Erweiterung ist nach dem Veröffentlichen auf einen Fehler gestoßen; die Änderung ist gespeichert.', + 'Änderungsprotokoll konnte nicht geschrieben werden; die Änderung ist gespeichert.', + ], $result['warnings']); + self::assertStringContainsString('listener kaputt', (string) file_get_contents($this->errorLog)); + } + + public function testWithoutAReadablePostTheRevIsNull(): void + { + Functions\when('get_post')->justReturn(null); + + $change = new PostChangeResult('lit/content-create', Mockery::mock(ChangeLog::class), 5, 'der Entwurf'); + + self::assertNull($change->reread()); + self::assertNull($change->result('ok', null)->toArray()['rev']); + } +} diff --git a/tests/Unit/Integrations/WordPress/Status/PostDuplicateAbilityTest.php b/tests/Unit/Integrations/WordPress/Status/PostDuplicateAbilityTest.php index 1f1542e..7585c8a 100644 --- a/tests/Unit/Integrations/WordPress/Status/PostDuplicateAbilityTest.php +++ b/tests/Unit/Integrations/WordPress/Status/PostDuplicateAbilityTest.php @@ -46,7 +46,7 @@ final class PostDuplicateAbilityTest extends TestCase $cloner->expects('duplicate')->with($this->original)->andReturn(77); Actions\expectDone('lit_mcp_after_save')->once()->with(77, 'lit/post-duplicate', ['post_id' => 5]); $log = Mockery::mock(ChangeLog::class); - $log->expects('record')->with('lit/post-duplicate', 'page', '77', 'Kopie von "Team" (ID 5) angelegt')->andReturn(8); + $log->expects('record')->with('lit/post-duplicate', 'page', '77', 'Kopie von "Team" (ID 5) angelegt', null)->andReturn(8); $result = $this->ability($cloner, $log)->execute(['post_id' => 5]); diff --git a/tests/Unit/Integrations/WordPress/Status/PostPublishAbilityTest.php b/tests/Unit/Integrations/WordPress/Status/PostPublishAbilityTest.php index 28d4efb..ca187ba 100644 --- a/tests/Unit/Integrations/WordPress/Status/PostPublishAbilityTest.php +++ b/tests/Unit/Integrations/WordPress/Status/PostPublishAbilityTest.php @@ -36,7 +36,7 @@ final class PostPublishAbilityTest extends TestCase Functions\expect('wp_update_post')->once()->with(['ID' => 5, 'post_status' => 'publish'], true)->andReturn(5); Functions\when('get_post_status')->justReturn('publish'); $log = Mockery::mock(ChangeLog::class); - $log->expects('record')->with('lit/post-publish', 'page', '5', '"Team" veröffentlicht')->andReturn(9); + $log->expects('record')->with('lit/post-publish', 'page', '5', '"Team" veröffentlicht', null)->andReturn(9); $result = $this->ability($log)->execute(['post_id' => 5]); diff --git a/tests/Unit/Integrations/WordPress/Status/PostTrashAbilityTest.php b/tests/Unit/Integrations/WordPress/Status/PostTrashAbilityTest.php index c8029d6..ab438b7 100644 --- a/tests/Unit/Integrations/WordPress/Status/PostTrashAbilityTest.php +++ b/tests/Unit/Integrations/WordPress/Status/PostTrashAbilityTest.php @@ -38,7 +38,7 @@ final class PostTrashAbilityTest extends TestCase Functions\expect('wp_trash_post')->once()->with(5)->andReturn(new WP_Post(['ID' => 5])); Functions\expect('wp_delete_post')->never(); $log = Mockery::mock(ChangeLog::class); - $log->expects('record')->with('lit/post-trash', 'page', '5', '"Alt" in den Papierkorb gelegt')->andReturn(2); + $log->expects('record')->with('lit/post-trash', 'page', '5', '"Alt" in den Papierkorb gelegt', null)->andReturn(2); $result = $this->ability($log)->execute(['post_id' => 5, 'confirm' => true]);