feat: lit/library-save creates library entries like Divi (E4)
Reuses NewDraft for insert and rollback (P6), requires edit_global_library for scope global (P7), refuses opaque and global roots before wrapping (P11) and checks the node with StoredNodeGuard. The source page is never written. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Library;
|
||||
|
||||
use LIT\MCP\Abilities\WriteFailed;
|
||||
use LIT\MCP\Integrations\WordPress\Content\NewDraft;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use WP_Error;
|
||||
use WP_Post;
|
||||
use WP_Term;
|
||||
|
||||
@@ -126,6 +131,50 @@ class DiviLibraryStore
|
||||
];
|
||||
}
|
||||
|
||||
/** Client text when the terms or meta of a new entry could not be written. */
|
||||
public const REASON_CREATE = 'Der Bibliothekseintrag konnte nicht vollständig angelegt werden.';
|
||||
|
||||
/**
|
||||
* A published entry with the terms and meta of $spec and empty content (E4; the layout follows
|
||||
* through DiviSaver::saveNew()). All or nothing through $draft (ruling P6, the transaction of
|
||||
* lit/content-create): a refused insert or missing taxonomies create nothing; a failed term or
|
||||
* meta write deletes the entry again (write_failed), or reports partial_write and logs it
|
||||
* when that delete fails; a throwing insert hook is a partial write without ID.
|
||||
*
|
||||
* @throws WriteFailed
|
||||
*/
|
||||
public function create(LibraryItemSpec $spec, NewDraft $draft): int
|
||||
{
|
||||
$missing = $this->missingTaxonomies();
|
||||
if ($missing !== []) {
|
||||
throw WriteFailed::rolledBack(
|
||||
'Die Divi-Bibliothek ist in dieser Anfrage nicht geladen.',
|
||||
detail: 'Divi Library taxonomies not registered: ' . implode(', ', $missing)
|
||||
);
|
||||
}
|
||||
|
||||
$id = $draft->insert([
|
||||
'post_type' => self::POST_TYPE,
|
||||
'post_status' => 'publish',
|
||||
'post_title' => $spec->title,
|
||||
'post_content' => '',
|
||||
]);
|
||||
if ($id instanceof WP_Error) {
|
||||
throw WriteFailed::rolledBack(
|
||||
'WordPress hat den Bibliothekseintrag abgelehnt.',
|
||||
detail: 'wp_insert_post refused the library entry: ' . $id->get_error_message()
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->describeNew($id, $spec);
|
||||
} catch (Throwable $e) {
|
||||
$draft->discard($id, $e, self::REASON_CREATE);
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Library taxonomies not registered in this request (F2: Divi registers them in
|
||||
* ET_Builder_Library; fixture finding 6: not in every context).
|
||||
@@ -140,6 +189,27 @@ class DiviLibraryStore
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Terms and meta of $spec on the new entry $id, each checked.
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function describeNew(int $id, LibraryItemSpec $spec): void
|
||||
{
|
||||
foreach ($spec->terms() as $taxonomy => $terms) {
|
||||
$result = wp_set_object_terms($id, $terms, $taxonomy);
|
||||
if ($result instanceof WP_Error) {
|
||||
throw new RuntimeException(sprintf('terms %s: %s', $taxonomy, $result->get_error_message()));
|
||||
}
|
||||
}
|
||||
foreach ($spec->meta() as $key => $value) {
|
||||
update_post_meta($id, $key, $value);
|
||||
if ((string) get_post_meta($id, $key, true) !== $value) {
|
||||
throw new RuntimeException(sprintf('meta %s of library entry %d not stored', $key, $id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return list<WP_Term> */
|
||||
private function terms(int $id, string $taxonomy): array
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user