$data wp_insert_post() data (unslashed). * @return int|WP_Error The new draft's ID, or WordPress' refusal as invalid input. * @throws WriteFailed When a hook threw during the insert. */ public function insert(array $data): int|WP_Error { try { $postId = wp_insert_post(wp_slash($data), true); } catch (Throwable $e) { // A hook threw – the row may or may not exist and its ID is unknown; nothing is // searched or deleted, the client is told to check (lit_partial_write without id). $this->logPartial('', 'Anlegen abgebrochen; Entwurf möglicherweise vorhanden'); throw WriteFailed::partial(self::REASON_INSERT, $e); } if ($postId instanceof WP_Error) { return Errors::invalidInput('WordPress hat den Eintrag abgelehnt: ' . $postId->get_error_message()); } return (int) $postId; } /** * Some checks need the draft: meta rights (add_post_meta needs its ID) and the Divi layout * against the new entry's own dynamic content options. A refusal removes the brand-new draft * again and is reported like any invalid input. * * @param array|WP_Error $denied fields. => error, or the error itself. * @throws WriteFailed When the draft cannot be deleted. */ public function refuse(int $postId, array|WP_Error $denied): WP_Error { try { $deleted = (bool) wp_delete_post($postId, true); } catch (Throwable $e) { $deleted = false; } if (! $deleted) { $summary = sprintf('Entwurf %d: Anlegen nicht zurückgenommen', $postId); $this->logPartial((string) $postId, $summary); $fields = $denied instanceof WP_Error ? Errors::fieldsOf($denied) : $denied; throw WriteFailed::partial( ($denied instanceof WP_Error ? 'Ungültiger Divi-Inhalt: ' : 'Keine Berechtigung für ') . implode(', ', array_keys($fields)) . '.', detail: sprintf('New post %d refused after the insert; draft could not be deleted', $postId), postId: $postId ); } return $denied instanceof WP_Error ? $denied : Errors::invalidInput('Eingaben sind ungültig; nichts wurde gespeichert.', $denied); } /** * Rolls the brand-new draft back: terms it created, then the draft itself (force – * it cannot have been used anywhere yet). $reason is the client text of the error. * * @throws WriteFailed Always. */ public function discard(int $postId, Throwable $cause, string $reason): never { $problems = Rollback::attempt( fn () => $this->writer->deleteCreatedTerms(), static fn () => self::deleteDraft($postId) ); $detail = sprintf('Extras of new post %d could not be written: %s', $postId, $cause->getMessage()); if ($problems === []) { throw WriteFailed::rolledBack($reason, $cause, $detail); } $summary = sprintf('Entwurf %d: Anlegen nur teilweise zurückgenommen', $postId); $this->logPartial((string) $postId, $summary); throw WriteFailed::partial($reason, $cause, $detail . '; rollback: ' . implode('; ', $problems), $postId); } /** @throws RuntimeException When the draft is not deleted. */ private static function deleteDraft(int $postId): void { try { $deleted = wp_delete_post($postId, true); } catch (Throwable $e) { throw new RuntimeException(sprintf('draft %d could not be deleted: %s', $postId, $e->getMessage()), 0, $e); } if (! $deleted) { throw new RuntimeException(sprintf('draft %d could not be deleted', $postId)); } } private function logPartial(string $postId, string $summary): void { $this->log->recordPartial($this->ability, $this->postType, $postId, $summary); } }