refactor: DiviSaver writes content through one guarded writeContent() (M4)
writeContent($postId, $content, ?$before) holds the kses window plus
wp_update_post(wp_slash([ID, post_content]), true); save() passes the restore
point capture as $before, so the window still holds exactly RevisionPoint::
capture() and wp_update_post() (S37), saveNew() and the restore() rollback use it
without. refusal() turns WP_Error / 0 into the message ("returned 0" as before)
for save() and saveNew(); restore() keeps its own RuntimeException text. The
class docblock (the M-5 window description) is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -68,20 +68,19 @@ class DiviSaver
|
|||||||
$revisionId = null;
|
$revisionId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = $this->withoutKses(static function () use ($postId, $content, &$revisionId): int|WP_Error {
|
// Ruling S37: the restore point is captured inside the same kses window.
|
||||||
|
$result = $this->writeContent($postId, $content, static function () use ($postId, &$revisionId): void {
|
||||||
$revisionId = RevisionPoint::capture($postId);
|
$revisionId = RevisionPoint::capture($postId);
|
||||||
|
|
||||||
return wp_update_post(wp_slash(['ID' => $postId, 'post_content' => $content]), true);
|
|
||||||
});
|
});
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$this->restore($postId, (string) $post->post_content, $revisionId, $e);
|
$this->restore($postId, (string) $post->post_content, $revisionId, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($result instanceof WP_Error || $result === 0) {
|
$refusal = self::refusal($result);
|
||||||
$message = $result instanceof WP_Error ? $result->get_error_message() : 'returned 0';
|
if ($refusal !== null) {
|
||||||
throw WriteFailed::rolledBack(
|
throw WriteFailed::rolledBack(
|
||||||
self::REASON,
|
self::REASON,
|
||||||
detail: sprintf('wp_update_post(%d) refused: %s', $postId, $message),
|
detail: sprintf('wp_update_post(%d) refused: %s', $postId, $refusal),
|
||||||
state: self::keeping($revisionId),
|
state: self::keeping($revisionId),
|
||||||
postId: $postId,
|
postId: $postId,
|
||||||
revisionId: $revisionId
|
revisionId: $revisionId
|
||||||
@@ -129,12 +128,9 @@ class DiviSaver
|
|||||||
*/
|
*/
|
||||||
public function saveNew(int $postId, string $content): array
|
public function saveNew(int $postId, string $content): array
|
||||||
{
|
{
|
||||||
$result = $this->withoutKses(
|
$refusal = self::refusal($this->writeContent($postId, $content));
|
||||||
static fn (): int|WP_Error => wp_update_post(wp_slash(['ID' => $postId, 'post_content' => $content]), true)
|
if ($refusal !== null) {
|
||||||
);
|
throw new RuntimeException(sprintf('wp_update_post(%d) refused the Divi layout: %s', $postId, $refusal));
|
||||||
if ($result instanceof WP_Error || $result === 0) {
|
|
||||||
$message = $result instanceof WP_Error ? $result->get_error_message() : 'returned 0';
|
|
||||||
throw new RuntimeException(sprintf('wp_update_post(%d) refused the Divi layout: %s', $postId, $message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DiviFlags::set($postId);
|
DiviFlags::set($postId);
|
||||||
@@ -144,6 +140,34 @@ class DiviSaver
|
|||||||
return $stored instanceof WP_Post && (string) $stored->post_content !== $content ? [self::CHANGED_ON_SAVE] : [];
|
return $stored instanceof WP_Post && (string) $stored->post_content !== $content ? [self::CHANGED_ON_SAVE] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The one guarded content write: wp_update_post() (wp_slash, status unchanged) of $content in
|
||||||
|
* the kses window. $before runs first inside the same window – save() captures the restore
|
||||||
|
* point there (ruling S37); nothing else ever runs in it (see the class docblock).
|
||||||
|
*
|
||||||
|
* @param (callable(): void)|null $before
|
||||||
|
*/
|
||||||
|
private function writeContent(int $postId, string $content, ?callable $before = null): int|WP_Error
|
||||||
|
{
|
||||||
|
return $this->withoutKses(static function () use ($postId, $content, $before): int|WP_Error {
|
||||||
|
if ($before !== null) {
|
||||||
|
$before();
|
||||||
|
}
|
||||||
|
|
||||||
|
return wp_update_post(wp_slash(['ID' => $postId, 'post_content' => $content]), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Why wp_update_post() refused ($result WP_Error or 0), null when it saved. */
|
||||||
|
private static function refusal(int|WP_Error $result): ?string
|
||||||
|
{
|
||||||
|
if ($result instanceof WP_Error) {
|
||||||
|
return $result->get_error_message();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result === 0 ? 'returned 0' : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs $write with kses' filters off for users without unfiltered_html (D7); restored in
|
* Runs $write with kses' filters off for users without unfiltered_html (D7); restored in
|
||||||
* finally, right when $write returns or throws.
|
* finally, right when $write returns or throws.
|
||||||
@@ -174,11 +198,7 @@ class DiviSaver
|
|||||||
$failures = Rollback::attempt(function () use ($postId, $before): void {
|
$failures = Rollback::attempt(function () use ($postId, $before): void {
|
||||||
PostGuard::assertNoMissedSchedule($postId, 'restoring would publish a missed schedule');
|
PostGuard::assertNoMissedSchedule($postId, 'restoring would publish a missed schedule');
|
||||||
|
|
||||||
$restored = $this->withoutKses(
|
if (self::refusal($this->writeContent($postId, $before)) !== null) {
|
||||||
static fn (): int|WP_Error
|
|
||||||
=> wp_update_post(wp_slash(['ID' => $postId, 'post_content' => $before]), true)
|
|
||||||
);
|
|
||||||
if ($restored instanceof WP_Error || $restored === 0) {
|
|
||||||
throw new RuntimeException('previous content could not be written back');
|
throw new RuntimeException('previous content could not be written back');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user