API: Added changelog support to page create/update API

Also changed the UI based forms to submit as changelog instead of
summary to align with more sensible name used on the API.
Added testing to cover.

For #6162
This commit is contained in:
Dan Brown
2026-08-28 10:48:04 +01:00
parent f1fe98d194
commit 4a871811e9
10 changed files with 66 additions and 26 deletions
@@ -27,6 +27,7 @@ class PageApiController extends ApiController
'markdown' => ['required_without:html', 'string'],
'tags' => ['array'],
'priority' => ['integer'],
'changelog' => ['string', 'min:1', 'max:180'],
],
'update' => [
'book_id' => ['integer'],
@@ -36,6 +37,7 @@ class PageApiController extends ApiController
'markdown' => ['string'],
'tags' => ['array'],
'priority' => ['integer'],
'changelog' => ['string', 'min:1', 'max:180'],
],
];
@@ -75,7 +77,7 @@ class PageApiController extends ApiController
*/
public function create(Request $request): JsonResponse
{
$this->validate($request, $this->rules['create']);
$validated = $this->validate($request, $this->rules['create']);
if ($request->has('chapter_id')) {
$parent = $this->entityQueries->chapters->findVisibleByIdOrFail(intval($request->input('chapter_id')));
@@ -85,7 +87,7 @@ class PageApiController extends ApiController
$this->checkOwnablePermission(Permission::PageCreate, $parent);
$draft = $this->pageRepo->getNewDraftPage($parent);
$this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create'])));
$this->pageRepo->publishDraft($draft, $validated);
return response()->json($draft->forJsonDisplay());
}
+2 -2
View File
@@ -91,7 +91,7 @@ class PageRepo
$draft = $this->baseRepo->update($draft, $input);
$draft->rebuildPermissions();
$summary = trim($input['summary'] ?? '') ?: trans('entities.pages_initial_revision');
$summary = trim($input['changelog'] ?? '') ?: trans('entities.pages_initial_revision');
$this->revisionRepo->storeNewForPage($draft, $summary);
$draft->refresh();
@@ -134,7 +134,7 @@ class PageRepo
$this->revisionRepo->deleteDraftsForCurrentUser($page);
// Save a revision after updating
$summary = trim($input['summary'] ?? '');
$summary = trim($input['changelog'] ?? '');
$htmlChanged = isset($input['html']) && $input['html'] !== $oldHtml;
$nameChanged = isset($input['name']) && $input['name'] !== $oldName;
$markdownChanged = isset($input['markdown']) && $input['markdown'] !== $oldMarkdown;
@@ -108,7 +108,7 @@
<p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
<textarea
refs="page-editor@changelogInput"
name="summary"
name="changelog"
id="summary-input"
rows="2"
maxlength="180"
+1 -1
View File
@@ -30,7 +30,7 @@ class WebhookFormatTesting extends TestCase
public function test_page_create_and_update_events_show_revision_info()
{
$page = $this->entities->page();
$this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
$this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'changelog' => 'Update a']);
$data = $this->getWebhookData(ActivityType::PAGE_UPDATE, $page);
$this->assertEquals($page->currentRevision->id, Arr::get($data, 'related_item.current_revision.id'));
+38
View File
@@ -128,6 +128,27 @@ class PagesApiTest extends TestCase
$this->assertStringContainsString('href="https://example.com"', $respHtml);
}
public function test_create_can_accept_a_changelog_message()
{
$this->actingAsApiEditor();
$book = $this->entities->book();
$details = [
'name' => 'My updated API page',
'html' => '<p>A page updated via the API</p>',
'changelog' => 'This is a create changelog message via the API',
'book_id' => $book->id,
];
$resp = $this->postJson($this->baseEndpoint, $details);
$resp->assertOk();
$pageId = $resp->json('id');
$page = Page::query()->findOrFail($pageId);
$latestRevision = $page->revisions()->orderBy('id', 'desc')->first();
$this->assertEquals('This is a create changelog message via the API', $latestRevision->summary);
}
public function test_read_endpoint()
{
$this->actingAsApiEditor();
@@ -338,6 +359,23 @@ class PagesApiTest extends TestCase
$this->assertGreaterThan(Carbon::now()->subDay()->unix(), $page->updated_at->unix());
}
public function test_update_can_accept_a_changelog_message()
{
$this->actingAsApiEditor();
$page = $this->entities->page();
$details = [
'name' => 'My updated API page',
'html' => '<p>A page updated via the API</p>',
'changelog' => 'This is a changelog message via the API',
];
$resp = $this->putJson($this->baseEndpoint . "/{$page->id}", $details);
$resp->assertOk();
$latestRevision = $page->revisions()->orderBy('id', 'desc')->first();
$this->assertEquals('This is a changelog message via the API', $latestRevision->summary);
}
public function test_delete_endpoint()
{
$this->actingAsApiEditor();
+2 -2
View File
@@ -14,8 +14,8 @@ class ClearRevisionsCommandTest extends TestCase
$this->asEditor();
$pageRepo = app(PageRepo::class);
$page = Page::first();
$pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
$pageRepo->updatePageDraft($page, ['name' => 'updated page', 'html' => '<p>new content in draft</p>', 'summary' => 'page revision testing']);
$pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>new content</p>', 'changelog' => 'page revision testing']);
$pageRepo->updatePageDraft($page, ['name' => 'updated page', 'html' => '<p>new content in draft</p>', 'changelog' => 'page revision testing']);
$this->assertDatabaseHas('page_revisions', [
'page_id' => $page->id,
+13 -13
View File
@@ -48,7 +48,7 @@ class PageContentTest extends TestCase
$includeTag = '{{@' . $secondPage->id . '}}';
$page->html = '<p>' . $includeTag . '</p>';
$resp = $this->put($page->getUrl(), ['name' => $page->name, 'html' => $page->html, 'summary' => '']);
$resp = $this->put($page->getUrl(), ['name' => $page->name, 'html' => $page->html, 'changelog' => '']);
$resp->assertStatus(302);
@@ -127,7 +127,7 @@ class PageContentTest extends TestCase
$pageSave = $this->put($page->getUrl(), [
'name' => $page->name,
'html' => $content,
'summary' => '',
'changelog' => '',
]);
$pageSave->assertRedirect();
@@ -144,7 +144,7 @@ class PageContentTest extends TestCase
$this->put($page->getUrl(), [
'name' => $page->name,
'html' => $content,
'summary' => '',
'changelog' => '',
]);
$updatedPage = Page::query()->where('id', '=', $page->id)->first();
@@ -253,7 +253,7 @@ class PageContentTest extends TestCase
| Paragraph | Text |';
$this->put($page->getUrl(), [
'name' => $page->name, 'markdown' => $content,
'html' => '', 'summary' => '',
'html' => '', 'changelog' => '',
]);
$page->refresh();
@@ -272,7 +272,7 @@ class PageContentTest extends TestCase
- [x] Item b';
$this->put($page->getUrl(), [
'name' => $page->name, 'markdown' => $content,
'html' => '', 'summary' => '',
'html' => '', 'changelog' => '',
]);
$page->refresh();
@@ -292,7 +292,7 @@ class PageContentTest extends TestCase
$content = '~~some crossed out text~~';
$this->put($page->getUrl(), [
'name' => $page->name, 'markdown' => $content,
'html' => '', 'summary' => '',
'html' => '', 'changelog' => '',
]);
$page->refresh();
@@ -311,7 +311,7 @@ class PageContentTest extends TestCase
$content = '<!-- Test Comment -->';
$this->put($page->getUrl(), [
'name' => $page->name, 'markdown' => $content,
'html' => '', 'summary' => '',
'html' => '', 'changelog' => '',
])->assertRedirect();
$page->refresh();
@@ -328,7 +328,7 @@ class PageContentTest extends TestCase
$page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'html' => '<p>test<img src="data:image/jpeg;base64,' . $this->base64Jpeg . '"/></p>',
]);
@@ -352,7 +352,7 @@ class PageContentTest extends TestCase
$base64PngWithWhitespace = "iVBORw0KGg\noAAAANSUhE\tUgAAAAEAAAA BCA YAAAAfFcSJAAA\n\t ACklEQVR4nGMAAQAABQAB";
$base64PngWithoutWhitespace = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQAB';
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'html' => '<p>test<img src="data:image/png;base64,' . $base64PngWithWhitespace . '"/></p>',
]);
@@ -381,7 +381,7 @@ class PageContentTest extends TestCase
$page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'html' => '<p>test<img src="data:image/' . $extension . ';base64,' . $this->base64Jpeg . '"/></p>',
]);
@@ -425,7 +425,7 @@ class PageContentTest extends TestCase
$page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'markdown' => 'test ![test](data:image/jpeg;base64,' . $this->base64Jpeg . ')',
]);
@@ -456,7 +456,7 @@ class PageContentTest extends TestCase
$base64Content = base64_encode($content);
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'markdown' => 'test ![test](data:image/jpeg;base64,' . $base64Content . ') ![test](data:image/jpeg;base64,' . $base64Content . ')',
]);
@@ -479,7 +479,7 @@ class PageContentTest extends TestCase
$page = $this->entities->page();
$this->asEditor()->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
'name' => $page->name, 'changelog' => '',
'markdown' => 'test ![test](data:image/jiff;base64,' . $this->base64Jpeg . ')',
]);
+2 -2
View File
@@ -119,7 +119,7 @@ class PageRevisionTest extends TestCase
{
$this->asEditor();
$page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'My first update']);
$this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'changelog' => 'My first update']);
$this->createRevisions($page, 1, ['html' => '<p>new content</p>']);
$page->refresh();
@@ -327,7 +327,7 @@ class PageRevisionTest extends TestCase
$user = user();
for ($i = 0; $i < $times; $i++) {
$data = ['name' => 'Page update' . $i, 'summary' => 'Update entry' . $i];
$data = ['name' => 'Page update' . $i, 'changelog' => 'Update entry' . $i];
if (!isset($attrs['markdown'])) {
$data['html'] = '<p>My update page</p>';
}
+1 -1
View File
@@ -98,7 +98,7 @@ class PageTest extends TestCase
$details = [
'html' => '<h1>a title</h1>',
'name' => 'My page with summary',
'summary' => 'Here is my changelog message for a new page!',
'changelog' => 'Here is my changelog message for a new page!',
];
$resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details);
$resp->assertRedirect();
+2 -2
View File
@@ -819,7 +819,7 @@ class ImageTest extends TestCase
$pageRepo->update($page, [
'name' => $page->name,
'html' => $page->html . "<img src=\"{$image->url}\">",
'summary' => '',
'changelog' => '',
]);
// Ensure no images are reported as deletable
@@ -831,7 +831,7 @@ class ImageTest extends TestCase
$pageRepo->update($page, [
'name' => $page->name,
'html' => '<p>Hello</p>',
'summary' => '',
'changelog' => '',
]);
// Ensure revision images are picked up okay