From 1dbc3588cf11c12fde3da0ca4cb951c55ce810d9 Mon Sep 17 00:00:00 2001
From: Lennert Daniels <lennert.daniels@gmail.com>
Date: Fri, 2 Dec 2022 18:41:59 +0100
Subject: [PATCH] Add default_template as Book setting

---
 app/Entities/Models/Book.php                  | 10 +++++-
 app/Http/Controllers/BookController.php       | 16 +++++++++-
 ...2_104541_add_default_template_to_books.php | 32 +++++++++++++++++++
 resources/lang/en/entities.php                |  2 ++
 resources/views/books/create.blade.php        |  5 ++-
 resources/views/books/edit.blade.php          |  6 +++-
 resources/views/books/parts/form.blade.php    |  9 ++++++
 .../views/entities/template-manager.blade.php | 10 ++++++
 8 files changed, 86 insertions(+), 4 deletions(-)
 create mode 100644 database/migrations/2022_12_02_104541_add_default_template_to_books.php
 create mode 100644 resources/views/entities/template-manager.blade.php

diff --git a/app/Entities/Models/Book.php b/app/Entities/Models/Book.php
index fc4556857..b84a351f8 100644
--- a/app/Entities/Models/Book.php
+++ b/app/Entities/Models/Book.php
@@ -27,7 +27,7 @@ class Book extends Entity implements HasCoverImage
 
     public $searchFactor = 1.2;
 
-    protected $fillable = ['name', 'description'];
+    protected $fillable = ['name', 'description', 'default_template'];
     protected $hidden = ['pivot', 'image_id', 'deleted_at'];
 
     /**
@@ -78,6 +78,14 @@ class Book extends Entity implements HasCoverImage
         return 'cover_book';
     }
 
+    /**
+     * Get the Page that is used as default template for newly created pages within this Book.
+     */
+    public function defaultTemplate(): BelongsTo
+    {
+        return $this->belongsTo(Page::class, 'default_template');
+    }
+
     /**
      * Get all pages within this book.
      */
diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index 14c3af1cc..9d8db27e9 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -6,6 +6,7 @@ use BookStack\Actions\ActivityQueries;
 use BookStack\Actions\ActivityType;
 use BookStack\Actions\View;
 use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Tools\Cloner;
@@ -79,8 +80,14 @@ class BookController extends Controller
 
         $this->setPageTitle(trans('entities.books_create'));
 
+        $templates = Page::visible()
+            ->where('template', '=', true)
+            ->orderBy('name', 'asc')
+            ->get();
+
         return view('books.create', [
             'bookshelf' => $bookshelf,
+            'templates' => $templates,
         ]);
     }
 
@@ -98,6 +105,7 @@ class BookController extends Controller
             'description' => ['string', 'max:1000'],
             'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
             'tags'        => ['array'],
+            'default_template'  => ['nullable', 'exists:pages,id'], 
         ]);
 
         $bookshelf = null;
@@ -151,7 +159,12 @@ class BookController extends Controller
         $this->checkOwnablePermission('book-update', $book);
         $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()]));
 
-        return view('books.edit', ['book' => $book, 'current' => $book]);
+        $templates = Page::visible()
+            ->where('template', '=', true)
+            ->orderBy('name', 'asc')
+            ->get();
+
+        return view('books.edit', ['book' => $book, 'current' => $book, 'templates' => $templates]);
     }
 
     /**
@@ -171,6 +184,7 @@ class BookController extends Controller
             'description' => ['string', 'max:1000'],
             'image'       => array_merge(['nullable'], $this->getImageValidationRules()),
             'tags'        => ['array'],
+            'default_template'  => ['nullable', 'exists:pages,id'], 
         ]);
 
         if ($request->has('image_reset')) {
diff --git a/database/migrations/2022_12_02_104541_add_default_template_to_books.php b/database/migrations/2022_12_02_104541_add_default_template_to_books.php
new file mode 100644
index 000000000..755f83b5c
--- /dev/null
+++ b/database/migrations/2022_12_02_104541_add_default_template_to_books.php
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddDefaultTemplateToBooks extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('books', function (Blueprint $table) {
+            $table->integer('default_template')->nullable();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('books', function (Blueprint $table) {
+            $table->dropColumn('default_template');
+        });
+    }
+}
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index fa2586f8d..38c2f2ae3 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -328,6 +328,8 @@ return [
     'templates_replace_content' => 'Replace page content',
     'templates_append_content' => 'Append to page content',
     'templates_prepend_content' => 'Prepend to page content',
+    'default_template' => 'Default Page Template',
+    'default_template_explain' => "Assign a default template that will be used for all new pages in this book.",
 
     // Profile View
     'profile_user_for_x' => 'User for :time',
diff --git a/resources/views/books/create.blade.php b/resources/views/books/create.blade.php
index eead4191c..6253a49bb 100644
--- a/resources/views/books/create.blade.php
+++ b/resources/views/books/create.blade.php
@@ -28,7 +28,10 @@
         <main class="content-wrap card">
             <h1 class="list-heading">{{ trans('entities.books_create') }}</h1>
             <form action="{{ isset($bookshelf) ? $bookshelf->getUrl('/create-book') : url('/books') }}" method="POST" enctype="multipart/form-data">
-                @include('books.parts.form', ['returnLocation' => isset($bookshelf) ? $bookshelf->getUrl() : url('/books')])
+                @include('books.parts.form', [
+                    'templates' => $templates, 
+                    'returnLocation' => isset($bookshelf) ? $bookshelf->getUrl() : url('/books')
+                ])
             </form>
         </main>
     </div>
diff --git a/resources/views/books/edit.blade.php b/resources/views/books/edit.blade.php
index 180500e0a..9ec472935 100644
--- a/resources/views/books/edit.blade.php
+++ b/resources/views/books/edit.blade.php
@@ -18,7 +18,11 @@
             <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()])
+                @include('books.parts.form', [
+                    'model' => $book, 
+                    'templates' => $templates,
+                    'returnLocation' => $book->getUrl()
+                ])
             </form>
         </main>
 
diff --git a/resources/views/books/parts/form.blade.php b/resources/views/books/parts/form.blade.php
index e893bcead..c6ef7d171 100644
--- a/resources/views/books/parts/form.blade.php
+++ b/resources/views/books/parts/form.blade.php
@@ -35,6 +35,15 @@
     </div>
 </div>
 
+<div class="form-group collapsible" component="collapsible" id="template-control">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+        <label for="template-manager">{{ trans('entities.default_template') }}</label>
+    </button>
+    <div refs="collapsible@content" class="collapse-content">
+        @include('entities.template-manager', ['entity' => $book ?? null, 'templates' => $templates])
+    </div>
+</div>
+
 <div class="form-group text-right">
     <a href="{{ $returnLocation }}" class="button outline">{{ trans('common.cancel') }}</a>
     <button type="submit" class="button">{{ trans('entities.books_save') }}</button>
diff --git a/resources/views/entities/template-manager.blade.php b/resources/views/entities/template-manager.blade.php
new file mode 100644
index 000000000..fe04d9389
--- /dev/null
+++ b/resources/views/entities/template-manager.blade.php
@@ -0,0 +1,10 @@
+<p class="text-muted small">
+    {!! nl2br(e(trans('entities.default_template_explain'))) !!}
+</p>
+
+<select name="default_template" id="default_template">
+    <option value="">---</option>
+    @foreach ($templates as $template)
+        <option @if(isset($entity) && $entity->default_template === $template->id) selected @endif value="{{ $template->id }}">{{ $template->name }}</option>
+    @endforeach
+</select>
\ No newline at end of file