]> BookStack Code Mirror - bookstack/commitdiff
Got book to shelf conversions working
authorDan Brown <redacted>
Wed, 15 Jun 2022 14:05:08 +0000 (15:05 +0100)
committerDan Brown <redacted>
Wed, 15 Jun 2022 14:05:08 +0000 (15:05 +0100)
- Also extracted shelf to book view elements to own partial.
- Fixed some existing logic including image param handling in update
  request and activity logging against correct element.

12 files changed:
app/Actions/ActivityType.php
app/Entities/Repos/BookshelfRepo.php
app/Entities/Repos/PageRepo.php
app/Entities/Tools/HierarchyTransformer.php
app/Http/Controllers/BookController.php
app/Http/Controllers/BookshelfController.php
resources/lang/en/activities.php
resources/views/books/edit.blade.php
resources/views/books/parts/convert-to-shelf.blade.php [new file with mode: 0644]
resources/views/chapters/edit.blade.php
resources/views/chapters/parts/convert-to-book.blade.php [new file with mode: 0644]
routes/web.php

index 997cc041ab177d5fd466fe58889704f66bd1a44c..0ad25a5ab7210127442960ad2897b52ed7740bad 100644 (file)
@@ -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';
 
index 03e7804d5db8552a1a1ed718c0ad7590242ba87c..f37db1f06b2728d9475dc8a6744d2240e1cfe8da 100644 (file)
@@ -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);
         }
 
index c106d2fd30679d6f663cdd845ddbaf1b0aee4fe5..e3c6bd17af95b7ea869ffaa9c70ad059d248ee10 100644 (file)
@@ -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.
index 7304962b379c9a4578db8598971053631f194906..93c5bb9bb7103fb12ab3a60f2d76ebc0d75b22fc 100644 (file)
@@ -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
index b9dd0e7993758afffc65baf54e76d8e78a32a2e5..937f7d28f1817670bf4af3a18135bbf0aaf2362e 100644 (file)
@@ -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());
+    }
 }
index ce2e508c87518a162f980473b67babdcfaa53a9f..2f966beed11cee6332549ae66b1f2f61e7ce1c24 100644 (file)
@@ -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']);
         }
 
index 0c3d2e70437e07baaff9b778debebdb8dbfbeade..edddf9aebcc6a4cf9c3e70f6b6ea4ebf98d646af 100644 (file)
@@ -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',
index 4039771213d49e57d9d6e9924c00b67ebee25c8e..180500e0a04ae737b1f86668d7f8a41c070919e9 100644 (file)
             ]])
         </div>
 
-        <main class="content-wrap card">
+        <main class="content-wrap card auto-height">
             <h1 class="list-heading">{{ trans('entities.books_edit') }}</h1>
             <form action="{{ $book->getUrl() }}" method="POST" enctype="multipart/form-data">
                 <input type="hidden" name="_method" value="PUT">
                 @include('books.parts.form', ['model' => $book, 'returnLocation' => $book->getUrl()])
             </form>
         </main>
+
+
+        @if(userCan('book-delete', $book) && userCan('book-create-all') && userCan('bookshelf-create-all'))
+            @include('books.parts.convert-to-shelf', ['book' => $book])
+        @endif
     </div>
 @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 (file)
index 0000000..700377a
--- /dev/null
@@ -0,0 +1,35 @@
+<div class="content-wrap card auto-height">
+    <h2 class="list-heading">Convert to Shelf</h2>
+    <p>
+        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.
+
+        <br><br>
+
+        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.
+    </p>
+    <div class="text-right">
+        <div component="dropdown" class="dropdown-container">
+            <button refs="dropdown@toggle" class="button outline" aria-haspopup="true" aria-expanded="false">Convert Book</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 book?
+                    <br>
+                    This cannot be as easily undone.
+                </li>
+                <li>
+                    <form action="{{ $book->getUrl('/convert-to-shelf') }}" method="POST">
+                        {!! csrf_field() !!}
+                        <button type="submit" class="text-primary text-item">{{ trans('common.confirm') }}</button>
+                    </form>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
\ No newline at end of file
index f6fd3cc6bf5c287d147e6a63690714fc09aad63f..36058eff83bb090428133cb1a13744f5e6fa7646 100644 (file)
             </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>
+        @if(userCan('chapter-delete', $chapter) && userCan('book-create-all'))
+            @include('chapters.parts.convert-to-book')
+        @endif
 
     </div>
 
diff --git a/resources/views/chapters/parts/convert-to-book.blade.php b/resources/views/chapters/parts/convert-to-book.blade.php
new file mode 100644 (file)
index 0000000..8a5d2a1
--- /dev/null
@@ -0,0 +1,28 @@
+<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>
\ No newline at end of file
index dfda9725351d51c16fa98e279dc37275461b5a0e..5e16e5333e184f2f37a8f99b9e241a5deb0fd151 100644 (file)
@@ -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']);