mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Added Default Templates for Chapters
This commit is contained in:
parent
b191d8f99f
commit
70bfebcd7c
@ -49,9 +49,10 @@ class ChapterController extends Controller
|
|||||||
public function store(Request $request, string $bookSlug)
|
public function store(Request $request, string $bookSlug)
|
||||||
{
|
{
|
||||||
$validated = $this->validate($request, [
|
$validated = $this->validate($request, [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'description_html' => ['string', 'max:2000'],
|
'description_html' => ['string', 'max:2000'],
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail();
|
$book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail();
|
||||||
@ -111,9 +112,10 @@ class ChapterController extends Controller
|
|||||||
public function update(Request $request, string $bookSlug, string $chapterSlug)
|
public function update(Request $request, string $bookSlug, string $chapterSlug)
|
||||||
{
|
{
|
||||||
$validated = $this->validate($request, [
|
$validated = $this->validate($request, [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'description_html' => ['string', 'max:2000'],
|
'description_html' => ['string', 'max:2000'],
|
||||||
'tags' => ['array'],
|
'tags' => ['array'],
|
||||||
|
'default_template_id' => ['nullable', 'integer'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
|
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
|
||||||
|
@ -6,6 +6,7 @@ use BookStack\Activity\Models\View;
|
|||||||
use BookStack\Activity\Tools\CommentTree;
|
use BookStack\Activity\Tools\CommentTree;
|
||||||
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
||||||
use BookStack\Entities\Models\Book;
|
use BookStack\Entities\Models\Book;
|
||||||
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Repos\PageRepo;
|
use BookStack\Entities\Repos\PageRepo;
|
||||||
use BookStack\Entities\Tools\BookContents;
|
use BookStack\Entities\Tools\BookContents;
|
||||||
@ -259,7 +260,9 @@ 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_id', '=', $page->id)->count() > 0;
|
$usedAsTemplate =
|
||||||
|
Book::query()->where('default_template_id', '=', $page->id)->count() > 0 ||
|
||||||
|
Chapter::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 +282,9 @@ 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_id', '=', $page->id)->count() > 0;
|
$usedAsTemplate =
|
||||||
|
Book::query()->where('default_template_id', '=', $page->id)->count() > 0 ||
|
||||||
|
Chapter::query()->where('default_template_id', '=', $page->id)->count() > 0;
|
||||||
|
|
||||||
return view('pages.delete', [
|
return view('pages.delete', [
|
||||||
'book' => $page->book,
|
'book' => $page->book,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Entities\Models;
|
namespace BookStack\Entities\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -11,6 +12,8 @@ use Illuminate\Support\Collection;
|
|||||||
*
|
*
|
||||||
* @property Collection<Page> $pages
|
* @property Collection<Page> $pages
|
||||||
* @property string $description
|
* @property string $description
|
||||||
|
* @property ?int $default_template_id
|
||||||
|
* @property ?Page $defaultTemplate
|
||||||
*/
|
*/
|
||||||
class Chapter extends BookChild
|
class Chapter extends BookChild
|
||||||
{
|
{
|
||||||
@ -48,6 +51,14 @@ class Chapter extends BookChild
|
|||||||
return url('/' . implode('/', $parts));
|
return url('/' . implode('/', $parts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Page that is used as default template for newly created pages within this Chapter.
|
||||||
|
*/
|
||||||
|
public function defaultTemplate(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Page::class, 'default_template_id');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the visible pages in this chapter.
|
* Get the visible pages in this chapter.
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ namespace BookStack\Entities\Repos;
|
|||||||
|
|
||||||
use BookStack\Activity\ActivityType;
|
use BookStack\Activity\ActivityType;
|
||||||
use BookStack\Entities\Models\Book;
|
use BookStack\Entities\Models\Book;
|
||||||
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Models\Chapter;
|
use BookStack\Entities\Models\Chapter;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Tools\BookContents;
|
use BookStack\Entities\Tools\BookContents;
|
||||||
@ -46,6 +47,7 @@ class ChapterRepo
|
|||||||
$chapter->book_id = $parentBook->id;
|
$chapter->book_id = $parentBook->id;
|
||||||
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
||||||
$this->baseRepo->create($chapter, $input);
|
$this->baseRepo->create($chapter, $input);
|
||||||
|
$this->updateChapterDefaultTemplate($chapter, intval($input['default_template_id'] ?? null));
|
||||||
Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
|
Activity::add(ActivityType::CHAPTER_CREATE, $chapter);
|
||||||
|
|
||||||
return $chapter;
|
return $chapter;
|
||||||
@ -57,6 +59,11 @@ class ChapterRepo
|
|||||||
public function update(Chapter $chapter, array $input): Chapter
|
public function update(Chapter $chapter, array $input): Chapter
|
||||||
{
|
{
|
||||||
$this->baseRepo->update($chapter, $input);
|
$this->baseRepo->update($chapter, $input);
|
||||||
|
|
||||||
|
if (array_key_exists('default_template_id', $input)) {
|
||||||
|
$this->updateChapterDefaultTemplate($chapter, intval($input['default_template_id']));
|
||||||
|
}
|
||||||
|
|
||||||
Activity::add(ActivityType::CHAPTER_UPDATE, $chapter);
|
Activity::add(ActivityType::CHAPTER_UPDATE, $chapter);
|
||||||
|
|
||||||
return $chapter;
|
return $chapter;
|
||||||
@ -101,6 +108,33 @@ class ChapterRepo
|
|||||||
return $parent;
|
return $parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the default page template used for this chapter.
|
||||||
|
* Checks that, if changing, the provided value is a valid template and the user
|
||||||
|
* has visibility of the provided page template id.
|
||||||
|
*/
|
||||||
|
protected function updateChapterDefaultTemplate(Chapter $chapter, int $templateId): void
|
||||||
|
{
|
||||||
|
$changing = $templateId !== intval($chapter->default_template_id);
|
||||||
|
if (!$changing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($templateId === 0) {
|
||||||
|
$chapter->default_template_id = null;
|
||||||
|
$chapter->save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$templateExists = Page::query()->visible()
|
||||||
|
->where('template', '=', true)
|
||||||
|
->where('id', '=', $templateId)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
$chapter->default_template_id = $templateExists ? $templateId : null;
|
||||||
|
$chapter->save();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a page parent entity via an identifier string in the format:
|
* Find a page parent entity via an identifier string in the format:
|
||||||
* {type}:{id}
|
* {type}:{id}
|
||||||
|
@ -136,7 +136,13 @@ class PageRepo
|
|||||||
$page->book_id = $parent->id;
|
$page->book_id = $parent->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaultTemplate = $page->book->defaultTemplate;
|
// check for chapter
|
||||||
|
if ($page->chapter_id) {
|
||||||
|
$defaultTemplate = $page->chapter->defaultTemplate;
|
||||||
|
} else {
|
||||||
|
$defaultTemplate = $page->book->defaultTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
if ($defaultTemplate && userCan('view', $defaultTemplate)) {
|
if ($defaultTemplate && userCan('view', $defaultTemplate)) {
|
||||||
$page->forceFill([
|
$page->forceFill([
|
||||||
'html' => $defaultTemplate->html,
|
'html' => $defaultTemplate->html,
|
||||||
|
@ -208,6 +208,12 @@ class TrashCan
|
|||||||
|
|
||||||
$page->forceDelete();
|
$page->forceDelete();
|
||||||
|
|
||||||
|
// Remove chapter template usages
|
||||||
|
Chapter::query()->where('default_template_id', '=', $page->id)
|
||||||
|
->update(['default_template_id' => null]);
|
||||||
|
|
||||||
|
$page->forceDelete();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddDefaultTemplateToChapters extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('chapters', function (Blueprint $table) {
|
||||||
|
$table->integer('default_template_id')->nullable()->default(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('chapters', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('default_template_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -192,6 +192,9 @@ return [
|
|||||||
'chapters_permissions_success' => 'Chapter Permissions Updated',
|
'chapters_permissions_success' => 'Chapter Permissions Updated',
|
||||||
'chapters_search_this' => 'Search this chapter',
|
'chapters_search_this' => 'Search this chapter',
|
||||||
'chapter_sort_book' => 'Sort Book',
|
'chapter_sort_book' => 'Sort Book',
|
||||||
|
'chapter_default_template' => 'Default Page Template',
|
||||||
|
'chapter_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this chapter. Keep in mind this will only be used if the page creator has view access to those chosen template page.',
|
||||||
|
'chapter_default_template_select' => 'Select a template page',
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
'page' => 'Page',
|
'page' => 'Page',
|
||||||
|
@ -22,6 +22,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group collapsible" component="collapsible" id="template-control">
|
||||||
|
<button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
|
||||||
|
<label for="template-manager">{{ trans('entities.chapter_default_template') }}</label>
|
||||||
|
</button>
|
||||||
|
<div refs="collapsible@content" class="collapse-content">
|
||||||
|
<div class="flex-container-row gap-l justify-space-between pb-xs wrap">
|
||||||
|
<p class="text-muted small my-none min-width-xs flex">
|
||||||
|
{{ trans('entities.chapter_default_template_explain') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="min-width-m">
|
||||||
|
@include('form.page-picker', [
|
||||||
|
'name' => 'default_template_id',
|
||||||
|
'placeholder' => trans('entities.chapter_default_template_select'),
|
||||||
|
'value' => $chapter->default_template_id ?? null,
|
||||||
|
'selectorEndpoint' => '/search/entity-selector-templates',
|
||||||
|
])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group text-right">
|
<div class="form-group text-right">
|
||||||
<a href="{{ isset($chapter) ? $chapter->getUrl() : $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
|
<a href="{{ isset($chapter) ? $chapter->getUrl() : $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
|
||||||
<button type="submit" class="button">{{ trans('entities.chapters_save') }}</button>
|
<button type="submit" class="button">{{ trans('entities.chapters_save') }}</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user