Review, publish, preview and undo changes through the API

Revisions now call publishing only what publishes: a project's status is
whether the work is active, and a service is published by its flag.

The fake identity server answers by token, so a test can sign in a second
person.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 08:29:48 +02:00
parent 4c4a2b3299
commit 5b46436641
18 changed files with 1899 additions and 13 deletions

53
app/Support/Publisher.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Support;
use App\Enums\PageStatus;
use App\Models\Page;
use App\Models\Project;
use App\Models\Service;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
/**
* Putting a record on the site, or taking it off, whichever fields that means
* for its kind: a page has a status, a project only a date, a service a flag
* and a date.
*/
final class Publisher
{
/**
* Live now, or from `$at` when it is in the future. Whatever review it was
* waiting for is answered either way: scheduling is the decision.
*/
public static function publish(Model $record, ?CarbonInterface $at = null): void
{
$at ??= now();
$record->forceFill(match (true) {
$record instanceof Page => ['status' => PageStatus::Published, 'published_at' => $at],
$record instanceof Project => ['published_at' => $at],
$record instanceof Service => ['is_published' => true, 'published_at' => $at],
default => throw new InvalidArgumentException($record::class.' is not published this way.'),
})->save();
// Publishing now closes the review by itself (HasReviews); a date in
// the future does not, so it is closed here.
if ($record->isAwaitingReview()) {
$record->closeReview();
}
}
public static function unpublish(Model $record): void
{
$record->forceFill(match (true) {
$record instanceof Page => ['status' => PageStatus::Draft],
$record instanceof Project => ['published_at' => null],
$record instanceof Service => ['is_published' => false],
default => throw new InvalidArgumentException($record::class.' is not published this way.'),
})->save();
}
}