mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-12 16:08:08 +00:00
Page Templates: Changed template field name, added API support
This commit is contained in:
parent
7ebe7d4e58
commit
4017048555
15 changed files with 67 additions and 37 deletions
app/Entities
Controllers
Models
Repos
Tools
database/migrations
dev/api
requests
responses
resources/views/books/parts
tests
|
@ -14,11 +14,9 @@ use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class BookApiController extends ApiController
|
class BookApiController extends ApiController
|
||||||
{
|
{
|
||||||
protected BookRepo $bookRepo;
|
public function __construct(
|
||||||
|
protected BookRepo $bookRepo
|
||||||
public function __construct(BookRepo $bookRepo)
|
) {
|
||||||
{
|
|
||||||
$this->bookRepo = $bookRepo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,7 +56,9 @@ class BookApiController extends ApiController
|
||||||
*/
|
*/
|
||||||
public function read(string $id)
|
public function read(string $id)
|
||||||
{
|
{
|
||||||
$book = Book::visible()->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy'])->findOrFail($id);
|
$book = Book::visible()
|
||||||
|
->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy'])
|
||||||
|
->findOrFail($id);
|
||||||
|
|
||||||
$contents = (new BookContents($book))->getTree(true, false)->all();
|
$contents = (new BookContents($book))->getTree(true, false)->all();
|
||||||
$contentsApiData = (new ApiEntityListFormatter($contents))
|
$contentsApiData = (new ApiEntityListFormatter($contents))
|
||||||
|
@ -116,12 +116,14 @@ class BookApiController extends ApiController
|
||||||
'description' => ['string', 'max:1000'],
|
'description' => ['string', 'max:1000'],
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
||||||
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
],
|
],
|
||||||
'update' => [
|
'update' => [
|
||||||
'name' => ['string', 'min:1', 'max:255'],
|
'name' => ['string', 'min:1', 'max:255'],
|
||||||
'description' => ['string', 'max:1000'],
|
'description' => ['string', 'max:1000'],
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
||||||
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,11 +92,11 @@ class BookController extends Controller
|
||||||
{
|
{
|
||||||
$this->checkPermission('book-create-all');
|
$this->checkPermission('book-create-all');
|
||||||
$validated = $this->validate($request, [
|
$validated = $this->validate($request, [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'description' => ['string', 'max:1000'],
|
'description' => ['string', 'max:1000'],
|
||||||
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
'default_template' => ['nullable', 'integer'],
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$bookshelf = null;
|
$bookshelf = null;
|
||||||
|
@ -167,11 +167,11 @@ class BookController extends Controller
|
||||||
$this->checkOwnablePermission('book-update', $book);
|
$this->checkOwnablePermission('book-update', $book);
|
||||||
|
|
||||||
$validated = $this->validate($request, [
|
$validated = $this->validate($request, [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'description' => ['string', 'max:1000'],
|
'description' => ['string', 'max:1000'],
|
||||||
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
'default_template' => ['nullable', 'integer'],
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($request->has('image_reset')) {
|
if ($request->has('image_reset')) {
|
||||||
|
|
|
@ -259,7 +259,7 @@ class PageController extends Controller
|
||||||
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
|
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
|
||||||
$this->checkOwnablePermission('page-delete', $page);
|
$this->checkOwnablePermission('page-delete', $page);
|
||||||
$this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()]));
|
$this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()]));
|
||||||
$usedAsTemplate = Book::query()->where('default_template', '=', $page->id)->count() > 0;
|
$usedAsTemplate = Book::query()->where('default_template_id', '=', $page->id)->count() > 0;
|
||||||
|
|
||||||
return view('pages.delete', [
|
return view('pages.delete', [
|
||||||
'book' => $page->book,
|
'book' => $page->book,
|
||||||
|
@ -279,7 +279,7 @@ class PageController extends Controller
|
||||||
$page = $this->pageRepo->getById($pageId);
|
$page = $this->pageRepo->getById($pageId);
|
||||||
$this->checkOwnablePermission('page-update', $page);
|
$this->checkOwnablePermission('page-update', $page);
|
||||||
$this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()]));
|
$this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()]));
|
||||||
$usedAsTemplate = Book::query()->where('default_template', '=', $page->id)->count() > 0;
|
$usedAsTemplate = Book::query()->where('default_template_id', '=', $page->id)->count() > 0;
|
||||||
|
|
||||||
return view('pages.delete', [
|
return view('pages.delete', [
|
||||||
'book' => $page->book,
|
'book' => $page->book,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use Illuminate\Support\Collection;
|
||||||
*
|
*
|
||||||
* @property string $description
|
* @property string $description
|
||||||
* @property int $image_id
|
* @property int $image_id
|
||||||
* @property ?int $default_template
|
* @property ?int $default_template_id
|
||||||
* @property Image|null $cover
|
* @property Image|null $cover
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $chapters
|
* @property \Illuminate\Database\Eloquent\Collection $chapters
|
||||||
* @property \Illuminate\Database\Eloquent\Collection $pages
|
* @property \Illuminate\Database\Eloquent\Collection $pages
|
||||||
|
@ -78,7 +78,7 @@ class Book extends Entity implements HasCoverImage
|
||||||
*/
|
*/
|
||||||
public function defaultTemplate(): BelongsTo
|
public function defaultTemplate(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Page::class, 'default_template');
|
return $this->belongsTo(Page::class, 'default_template_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -86,6 +86,7 @@ class BookRepo
|
||||||
$book = new Book();
|
$book = new Book();
|
||||||
$this->baseRepo->create($book, $input);
|
$this->baseRepo->create($book, $input);
|
||||||
$this->baseRepo->updateCoverImage($book, $input['image'] ?? null);
|
$this->baseRepo->updateCoverImage($book, $input['image'] ?? null);
|
||||||
|
$this->updateBookDefaultTemplate($book, intval($input['default_template_id'] ?? null));
|
||||||
Activity::add(ActivityType::BOOK_CREATE, $book);
|
Activity::add(ActivityType::BOOK_CREATE, $book);
|
||||||
|
|
||||||
return $book;
|
return $book;
|
||||||
|
@ -98,8 +99,8 @@ class BookRepo
|
||||||
{
|
{
|
||||||
$this->baseRepo->update($book, $input);
|
$this->baseRepo->update($book, $input);
|
||||||
|
|
||||||
if (array_key_exists('default_template', $input)) {
|
if (array_key_exists('default_template_id', $input)) {
|
||||||
$this->updateBookDefaultTemplate($book, intval($input['default_template']));
|
$this->updateBookDefaultTemplate($book, intval($input['default_template_id']));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('image', $input)) {
|
if (array_key_exists('image', $input)) {
|
||||||
|
@ -118,13 +119,13 @@ class BookRepo
|
||||||
*/
|
*/
|
||||||
protected function updateBookDefaultTemplate(Book $book, int $templateId): void
|
protected function updateBookDefaultTemplate(Book $book, int $templateId): void
|
||||||
{
|
{
|
||||||
$changing = $templateId !== intval($book->default_template);
|
$changing = $templateId !== intval($book->default_template_id);
|
||||||
if (!$changing) {
|
if (!$changing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($templateId === 0) {
|
if ($templateId === 0) {
|
||||||
$book->default_template = null;
|
$book->default_template_id = null;
|
||||||
$book->save();
|
$book->save();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,7 @@ class BookRepo
|
||||||
->where('id', '=', $templateId)
|
->where('id', '=', $templateId)
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
$book->default_template = $templateExists ? $templateId : null;
|
$book->default_template_id = $templateExists ? $templateId : null;
|
||||||
$book->save();
|
$book->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,8 +203,8 @@ class TrashCan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove book template usages
|
// Remove book template usages
|
||||||
Book::query()->where('default_template', '=', $page->id)
|
Book::query()->where('default_template_id', '=', $page->id)
|
||||||
->update(['default_template' => null]);
|
->update(['default_template_id' => null]);
|
||||||
|
|
||||||
$page->forceDelete();
|
$page->forceDelete();
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class AddDefaultTemplateToBooks extends Migration
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
Schema::table('books', function (Blueprint $table) {
|
Schema::table('books', function (Blueprint $table) {
|
||||||
$table->integer('default_template')->nullable()->default(null);
|
$table->integer('default_template_id')->nullable()->default(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class AddDefaultTemplateToBooks extends Migration
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
Schema::table('books', function (Blueprint $table) {
|
Schema::table('books', function (Blueprint $table) {
|
||||||
$table->dropColumn('default_template');
|
$table->dropColumn('default_template_id');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "My own book",
|
"name": "My own book",
|
||||||
"description": "This is my own little book"
|
"description": "This is my own little book",
|
||||||
|
"default_template_id": 12,
|
||||||
|
"tags": [
|
||||||
|
{"name": "Category", "value": "Top Content"},
|
||||||
|
{"name": "Rating", "value": "Highest"}
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "My updated book",
|
"name": "My updated book",
|
||||||
"description": "This is my book with updated details"
|
"description": "This is my book with updated details",
|
||||||
|
"default_template_id": 12,
|
||||||
|
"tags": [
|
||||||
|
{"name": "Subject", "value": "Updates"}
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
"created_by": 1,
|
"created_by": 1,
|
||||||
"updated_by": 1,
|
"updated_by": 1,
|
||||||
"owned_by": 1,
|
"owned_by": 1,
|
||||||
|
"default_template_id": 12,
|
||||||
"updated_at": "2020-01-12T14:05:11.000000Z",
|
"updated_at": "2020-01-12T14:05:11.000000Z",
|
||||||
"created_at": "2020-01-12T14:05:11.000000Z"
|
"created_at": "2020-01-12T14:05:11.000000Z"
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@
|
||||||
"name": "Admin",
|
"name": "Admin",
|
||||||
"slug": "admin"
|
"slug": "admin"
|
||||||
},
|
},
|
||||||
|
"default_template_id": null,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
"id": 50,
|
"id": 50,
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"id": 16,
|
"id": 16,
|
||||||
"name": "My own book",
|
"name": "My updated book",
|
||||||
"slug": "my-own-book",
|
"slug": "my-updated-book",
|
||||||
"description": "This is my own little book - updated",
|
"description": "This is my book with updated details",
|
||||||
"created_at": "2020-01-12T14:09:59.000000Z",
|
"created_at": "2020-01-12T14:09:59.000000Z",
|
||||||
"updated_at": "2020-01-12T14:16:10.000000Z",
|
"updated_at": "2020-01-12T14:16:10.000000Z",
|
||||||
"created_by": 1,
|
"created_by": 1,
|
||||||
"updated_by": 1,
|
"updated_by": 1,
|
||||||
"owned_by": 1
|
"owned_by": 1,
|
||||||
|
"default_template_id": 12
|
||||||
}
|
}
|
|
@ -47,9 +47,9 @@
|
||||||
|
|
||||||
|
|
||||||
@include('form.page-picker', [
|
@include('form.page-picker', [
|
||||||
'name' => 'default_template',
|
'name' => 'default_template_id',
|
||||||
'placeholder' => trans('entities.books_default_template_select'),
|
'placeholder' => trans('entities.books_default_template_select'),
|
||||||
'value' => $book?->default_template ?? null,
|
'value' => $book?->default_template_id ?? null,
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,16 @@ class BooksApiTest extends TestCase
|
||||||
public function test_create_endpoint()
|
public function test_create_endpoint()
|
||||||
{
|
{
|
||||||
$this->actingAsApiEditor();
|
$this->actingAsApiEditor();
|
||||||
|
$templatePage = $this->entities->templatePage();
|
||||||
$details = [
|
$details = [
|
||||||
'name' => 'My API book',
|
'name' => 'My API book',
|
||||||
'description' => 'A book created via the API',
|
'description' => 'A book created via the API',
|
||||||
|
'default_template_id' => $templatePage->id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$resp = $this->postJson($this->baseEndpoint, $details);
|
$resp = $this->postJson($this->baseEndpoint, $details);
|
||||||
$resp->assertStatus(200);
|
$resp->assertStatus(200);
|
||||||
|
|
||||||
$newItem = Book::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
|
$newItem = Book::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
|
||||||
$resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
|
$resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
|
||||||
$this->assertActivityExists('book_create', $newItem);
|
$this->assertActivityExists('book_create', $newItem);
|
||||||
|
@ -83,6 +86,7 @@ class BooksApiTest extends TestCase
|
||||||
'owned_by' => [
|
'owned_by' => [
|
||||||
'name' => $book->ownedBy->name,
|
'name' => $book->ownedBy->name,
|
||||||
],
|
],
|
||||||
|
'default_template_id' => null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +125,11 @@ class BooksApiTest extends TestCase
|
||||||
{
|
{
|
||||||
$this->actingAsApiEditor();
|
$this->actingAsApiEditor();
|
||||||
$book = $this->entities->book();
|
$book = $this->entities->book();
|
||||||
|
$templatePage = $this->entities->templatePage();
|
||||||
$details = [
|
$details = [
|
||||||
'name' => 'My updated API book',
|
'name' => 'My updated API book',
|
||||||
'description' => 'A book created via the API',
|
'description' => 'A book created via the API',
|
||||||
|
'default_template_id' => $templatePage->id,
|
||||||
];
|
];
|
||||||
|
|
||||||
$resp = $this->putJson($this->baseEndpoint . "/{$book->id}", $details);
|
$resp = $this->putJson($this->baseEndpoint . "/{$book->id}", $details);
|
||||||
|
|
|
@ -53,6 +53,15 @@ class EntityProvider
|
||||||
return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
|
return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function templatePage(): Page
|
||||||
|
{
|
||||||
|
$page = $this->page();
|
||||||
|
$page->template = true;
|
||||||
|
$page->save();
|
||||||
|
|
||||||
|
return $page;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an un-fetched chapter from the system.
|
* Get an un-fetched chapter from the system.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Reference in a new issue