- Added required UI within edit view.
- Added required routes and controller actions.
const CHAPTER_MOVE = 'chapter_move';
const BOOK_CREATE = 'book_create';
+ const BOOK_CREATE_FROM_CHAPTER = 'book_create_from_chapter';
const BOOK_UPDATE = 'book_update';
const BOOK_DELETE = 'book_delete';
const BOOK_SORT = 'book_sort';
{
$book = new Book();
$this->baseRepo->create($book, $input);
- $this->baseRepo->updateCoverImage($book, $input['image']);
+ $this->baseRepo->updateCoverImage($book, $input['image'] ?? null);
Activity::add(ActivityType::BOOK_CREATE, $book);
return $book;
{
$this->baseRepo->update($book, $input);
- if (isset($input['image'])) {
+ if (array_key_exists('image', $input)) {
$this->baseRepo->updateCoverImage($book, $input['image'], $input['image'] === null);
}
namespace BookStack\Entities\Tools;
+use BookStack\Actions\ActivityType;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Repos\BookshelfRepo;
+use BookStack\Facades\Activity;
class HierarchyTransformer
{
protected Cloner $cloner;
protected TrashCan $trashCan;
+ public function __construct(BookRepo $bookRepo, BookshelfRepo $shelfRepo, Cloner $cloner, TrashCan $trashCan)
+ {
+ $this->bookRepo = $bookRepo;
+ $this->shelfRepo = $shelfRepo;
+ $this->cloner = $cloner;
+ $this->trashCan = $trashCan;
+ }
+
+ /**
+ * Transform a chapter into a book.
+ * Does not check permissions, check before calling.
+ */
public function transformChapterToBook(Chapter $chapter): Book
{
- // TODO - Check permissions before call
- // Permissions: edit-chapter, delete-chapter, create-book
$inputData = $this->cloner->entityToInputData($chapter);
$book = $this->bookRepo->create($inputData);
$this->cloner->copyEntityPermissions($chapter, $book);
$this->trashCan->destroyEntity($chapter);
- // TODO - Log activity for change
+ Activity::add(ActivityType::BOOK_CREATE_FROM_CHAPTER);
return $book;
}
use BookStack\Entities\Repos\ChapterRepo;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\Cloner;
+use BookStack\Entities\Tools\HierarchyTransformer;
use BookStack\Entities\Tools\NextPreviousContentLocator;
use BookStack\Entities\Tools\PermissionsUpdater;
use BookStack\Exceptions\MoveOperationException;
return redirect($chapter->getUrl());
}
+
+
+ /**
+ * Convert the chapter to a book.
+ */
+ public function convertToBook(HierarchyTransformer $transformer, string $bookSlug, string $chapterSlug)
+ {
+ $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
+ $this->checkOwnablePermission('chapter-update', $chapter);
+ $this->checkOwnablePermission('chapter-delete', $chapter);
+ $this->checkPermission('book-create-all');
+
+ $book = $transformer->transformChapterToBook($chapter);
+
+ return redirect($book->getUrl());
+ }
}
// Books
'book_create' => 'created book',
'book_create_notification' => 'Book successfully created',
+ 'book_create_from_chapter' => 'converted chapter to book',
+ 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book',
'book_update' => 'updated book',
'book_update_notification' => 'Book successfully updated',
'book_delete' => 'deleted book',
]])
</div>
- <main class="content-wrap card">
+ <main class="content-wrap card auto-height">
<h1 class="list-heading">{{ trans('entities.chapters_edit') }}</h1>
<form action="{{ $chapter->getUrl() }}" method="POST">
<input type="hidden" name="_method" value="PUT">
</form>
</main>
+{{-- TODO - Permissions--}}
+ <div class="content-wrap card auto-height">
+ <h2 class="list-heading">Convert to Book</h2>
+ <div class="grid half left-focus no-row-gap">
+ <p>
+ You can convert this chapter to a new book with the same contents.
+ Any permissions set on this chapter will be copied to the new book but any inherited permissions,
+ from the parent book, will not be copied which could lead to a change of access control.
+ </p>
+ <div class="text-m-right">
+ <div component="dropdown" class="dropdown-container">
+ <button refs="dropdown@toggle" class="button outline" aria-haspopup="true" aria-expanded="false">Convert Chapter</button>
+ <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
+ <li class="px-m py-s text-small text-muted">
+ Are you sure you want to convert this chapter?
+ <br>
+ This cannot be as easily undone.
+ </li>
+ <li>
+ <form action="{{ $chapter->getUrl('/convert-to-book') }}" method="POST">
+ {!! csrf_field() !!}
+ <button type="submit" class="text-primary text-item">{{ trans('common.confirm') }}</button>
+ </form>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+
</div>
@stop
\ No newline at end of file
Route::get('/books/{bookSlug}/chapter/{chapterSlug}/copy', [ChapterController::class, 'showCopy']);
Route::post('/books/{bookSlug}/chapter/{chapterSlug}/copy', [ChapterController::class, 'copy']);
Route::get('/books/{bookSlug}/chapter/{chapterSlug}/edit', [ChapterController::class, 'edit']);
+ Route::post('/books/{bookSlug}/chapter/{chapterSlug}/convert-to-book', [ChapterController::class, 'convertToBook']);
Route::get('/books/{bookSlug}/chapter/{chapterSlug}/permissions', [ChapterController::class, 'showPermissions']);
Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/pdf', [ChapterExportController::class, 'pdf']);
Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/html', [ChapterExportController::class, 'html']);