diff --git a/app/Actions/ActivityType.php b/app/Actions/ActivityType.php
index 997cc041a..0ad25a5ab 100644
--- a/app/Actions/ActivityType.php
+++ b/app/Actions/ActivityType.php
@@ -22,6 +22,7 @@ class ActivityType
const BOOK_SORT = 'book_sort';
const BOOKSHELF_CREATE = 'bookshelf_create';
+ const BOOKSHELF_CREATE_FROM_BOOK = 'bookshelf_create_from_book';
const BOOKSHELF_UPDATE = 'bookshelf_update';
const BOOKSHELF_DELETE = 'bookshelf_delete';
diff --git a/app/Entities/Repos/BookshelfRepo.php b/app/Entities/Repos/BookshelfRepo.php
index 03e7804d5..f37db1f06 100644
--- a/app/Entities/Repos/BookshelfRepo.php
+++ b/app/Entities/Repos/BookshelfRepo.php
@@ -89,7 +89,7 @@ class BookshelfRepo
{
$shelf = new Bookshelf();
$this->baseRepo->create($shelf, $input);
- $this->baseRepo->updateCoverImage($shelf, $input['image']);
+ $this->baseRepo->updateCoverImage($shelf, $input['image'] ?? null);
$this->updateBooks($shelf, $bookIds);
Activity::add(ActivityType::BOOKSHELF_CREATE, $shelf);
@@ -107,7 +107,7 @@ class BookshelfRepo
$this->updateBooks($shelf, $bookIds);
}
- if (isset($input['image'])) {
+ if (array_key_exists('image', $input)) {
$this->baseRepo->updateCoverImage($shelf, $input['image'], $input['image'] === null);
}
diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php
index c106d2fd3..e3c6bd17a 100644
--- a/app/Entities/Repos/PageRepo.php
+++ b/app/Entities/Repos/PageRepo.php
@@ -392,23 +392,6 @@ class PageRepo
return $parentClass::visible()->where('id', '=', $entityId)->first();
}
- /**
- * Change the page's parent to the given entity.
- */
- protected function changeParent(Page $page, Entity $parent)
- {
- $book = ($parent instanceof Chapter) ? $parent->book : $parent;
- $page->chapter_id = ($parent instanceof Chapter) ? $parent->id : 0;
- $page->save();
-
- if ($page->book->id !== $book->id) {
- $page->changeBook($book->id);
- }
-
- $page->load('book');
- $book->rebuildPermissions();
- }
-
/**
* Get a page revision to update for the given page.
* Checks for an existing revisions before providing a fresh one.
diff --git a/app/Entities/Tools/HierarchyTransformer.php b/app/Entities/Tools/HierarchyTransformer.php
index 7304962b3..93c5bb9bb 100644
--- a/app/Entities/Tools/HierarchyTransformer.php
+++ b/app/Entities/Tools/HierarchyTransformer.php
@@ -44,14 +44,16 @@ class HierarchyTransformer
$this->trashCan->destroyEntity($chapter);
- Activity::add(ActivityType::BOOK_CREATE_FROM_CHAPTER);
+ Activity::add(ActivityType::BOOK_CREATE_FROM_CHAPTER, $book);
return $book;
}
+ /**
+ * Transform a book into a shelf.
+ * Does not check permissions, check before calling.
+ */
public function transformBookToShelf(Book $book): Bookshelf
{
- // TODO - Check permissions before call
- // Permissions: edit-book, delete-book, create-shelf
$inputData = $this->cloner->entityToInputData($book);
$shelf = $this->shelfRepo->create($inputData, []);
$this->cloner->copyEntityPermissions($book, $shelf);
@@ -62,17 +64,22 @@ class HierarchyTransformer
foreach ($book->chapters as $index => $chapter) {
$newBook = $this->transformChapterToBook($chapter);
$shelfBookSyncData[$newBook->id] = ['order' => $index];
+ if (!$newBook->restricted) {
+ $this->cloner->copyEntityPermissions($shelf, $newBook);
+ }
}
- $shelf->books()->sync($shelfBookSyncData);
-
if ($book->directPages->count() > 0) {
$book->name .= ' ' . trans('entities.pages');
+ $shelfBookSyncData[$book->id] = ['order' => count($shelfBookSyncData) + 1];
+ $book->save();
} else {
$this->trashCan->destroyEntity($book);
}
- // TODO - Log activity for change
+ $shelf->books()->sync($shelfBookSyncData);
+
+ Activity::add(ActivityType::BOOKSHELF_CREATE_FROM_BOOK, $shelf);
return $shelf;
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index b9dd0e799..937f7d28f 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -9,6 +9,7 @@ use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\Cloner;
+use BookStack\Entities\Tools\HierarchyTransformer;
use BookStack\Entities\Tools\PermissionsUpdater;
use BookStack\Entities\Tools\ShelfContext;
use BookStack\Exceptions\ImageUploadException;
@@ -166,7 +167,7 @@ class BookController extends Controller
if ($request->has('image_reset')) {
$validated['image'] = null;
- } else if (is_null($validated['image'])) {
+ } else if (array_key_exists('image', $validated) && is_null($validated['image'])) {
unset($validated['image']);
}
@@ -266,4 +267,20 @@ class BookController extends Controller
return redirect($bookCopy->getUrl());
}
+
+ /**
+ * Convert the chapter to a book.
+ */
+ public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug)
+ {
+ $book = $this->bookRepo->getBySlug($bookSlug);
+ $this->checkOwnablePermission('book-update', $book);
+ $this->checkOwnablePermission('book-delete', $book);
+ $this->checkPermission('bookshelf-create-all');
+ $this->checkPermission('book-create-all');
+
+ $shelf = $transformer->transformBookToShelf($book);
+
+ return redirect($shelf->getUrl());
+ }
}
diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php
index ce2e508c8..2f966beed 100644
--- a/app/Http/Controllers/BookshelfController.php
+++ b/app/Http/Controllers/BookshelfController.php
@@ -167,7 +167,7 @@ class BookshelfController extends Controller
if ($request->has('image_reset')) {
$validated['image'] = null;
- } else if (is_null($validated['image'])) {
+ } else if (array_key_exists('image', $validated) && is_null($validated['image'])) {
unset($validated['image']);
}
diff --git a/resources/lang/en/activities.php b/resources/lang/en/activities.php
index 0c3d2e704..edddf9aeb 100644
--- a/resources/lang/en/activities.php
+++ b/resources/lang/en/activities.php
@@ -40,6 +40,8 @@ return [
// Bookshelves
'bookshelf_create' => 'created bookshelf',
'bookshelf_create_notification' => 'Bookshelf successfully created',
+ 'bookshelf_create_from_book' => 'converted book to bookshelf',
+ 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf',
'bookshelf_update' => 'updated bookshelf',
'bookshelf_update_notification' => 'Bookshelf successfully updated',
'bookshelf_delete' => 'deleted bookshelf',
diff --git a/resources/views/books/edit.blade.php b/resources/views/books/edit.blade.php
index 403977121..180500e0a 100644
--- a/resources/views/books/edit.blade.php
+++ b/resources/views/books/edit.blade.php
@@ -14,12 +14,17 @@
]])
-
+
{{ trans('entities.books_edit') }}
+
+
+ @if(userCan('book-delete', $book) && userCan('book-create-all') && userCan('bookshelf-create-all'))
+ @include('books.parts.convert-to-shelf', ['book' => $book])
+ @endif
@stop
\ No newline at end of file
diff --git a/resources/views/books/parts/convert-to-shelf.blade.php b/resources/views/books/parts/convert-to-shelf.blade.php
new file mode 100644
index 000000000..700377a19
--- /dev/null
+++ b/resources/views/books/parts/convert-to-shelf.blade.php
@@ -0,0 +1,35 @@
+
+
Convert to Shelf
+
+ You can convert this book to a new shelf with the same contents.
+ Chapters contained within this book will be converted to new books.
+
+ If this book contains any pages, that are not in a chapter, this book will be renamed
+ and contain such pages, and this book will become part of the new shelf.
+
+
+
+ Any permissions set on this book will be copied to the new shelf and to all new child books
+ that don't have their own permissions enforced.
+
+ Note that permissions on shelves do not auto-cascade to content within, as they do for books.
+
+
+
+
+
+
+ Are you sure you want to convert this book?
+
+ This cannot be as easily undone.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/views/chapters/edit.blade.php b/resources/views/chapters/edit.blade.php
index f6fd3cc6b..36058eff8 100644
--- a/resources/views/chapters/edit.blade.php
+++ b/resources/views/chapters/edit.blade.php
@@ -23,35 +23,9 @@
-{{-- TODO - Permissions--}}
-
-
Convert to Book
-
-
- 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.
-
-
-
-
-
-
- Are you sure you want to convert this chapter?
-
- This cannot be as easily undone.
-
+ 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.
+
+
+
+
+
+
+ Are you sure you want to convert this chapter?
+
+ This cannot be as easily undone.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index dfda97253..5e16e5333 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -82,6 +82,7 @@ Route::middleware('auth')->group(function () {
Route::get('/books/{slug}/delete', [BookController::class, 'showDelete']);
Route::get('/books/{bookSlug}/copy', [BookController::class, 'showCopy']);
Route::post('/books/{bookSlug}/copy', [BookController::class, 'copy']);
+ Route::post('/books/{bookSlug}/convert-to-shelf', [BookController::class, 'convertToShelf']);
Route::get('/books/{bookSlug}/sort', [BookSortController::class, 'show']);
Route::put('/books/{bookSlug}/sort', [BookSortController::class, 'update']);
Route::get('/books/{bookSlug}/export/html', [BookExportController::class, 'html']);