]> BookStack Code Mirror - bookstack/commitdiff
Added page navigation and tweaked header styles
authorDan Brown <redacted>
Sun, 18 Sep 2016 13:49:36 +0000 (14:49 +0100)
committerDan Brown <redacted>
Sun, 18 Sep 2016 13:49:36 +0000 (14:49 +0100)
Changed header selection in editor to be more descriptive and
to provide a wider range of styles.

Closes #68

app/Http/Controllers/PageController.php
app/Repos/PageRepo.php
resources/assets/js/pages/page-form.js
resources/assets/sass/_lists.scss
resources/assets/sass/_text.scss
resources/views/books/list-item.blade.php
resources/views/chapters/list-item.blade.php
resources/views/home.blade.php
resources/views/pages/list-item.blade.php
resources/views/pages/show.blade.php
resources/views/pages/sidebar-tree-list.blade.php

index 1509ace9563b7aee65fe3b8c167f5e07117bacf6..3d6abe5b4e97ea6f33041d2402f8c2002677333a 100644 (file)
@@ -42,7 +42,7 @@ class PageController extends Controller
 
     /**
      * Show the form for creating a new page.
-     * @param      $bookSlug
+     * @param string $bookSlug
      * @param bool $chapterSlug
      * @return Response
      * @internal param bool $pageSlug
@@ -61,8 +61,8 @@ class PageController extends Controller
 
     /**
      * Show form to continue editing a draft page.
-     * @param $bookSlug
-     * @param $pageId
+     * @param string $bookSlug
+     * @param int $pageId
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
     public function editDraft($bookSlug, $pageId)
@@ -112,8 +112,8 @@ class PageController extends Controller
      * Display the specified page.
      * If the page is not found via the slug the
      * revisions are searched for a match.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return Response
      */
     public function show($bookSlug, $pageSlug)
@@ -131,14 +131,17 @@ class PageController extends Controller
         $this->checkOwnablePermission('page-view', $page);
 
         $sidebarTree = $this->bookRepo->getChildren($book);
+        $pageNav = $this->pageRepo->getPageNav($page);
+        
         Views::add($page);
         $this->setPageTitle($page->getShortName());
-        return view('pages/show', ['page' => $page, 'book' => $book, 'current' => $page, 'sidebarTree' => $sidebarTree]);
+        return view('pages/show', ['page' => $page, 'book' => $book,
+                                   'current' => $page, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav]);
     }
 
     /**
      * Get page from an ajax request.
-     * @param $pageId
+     * @param int $pageId
      * @return \Illuminate\Http\JsonResponse
      */
     public function getPageAjax($pageId)
@@ -149,8 +152,8 @@ class PageController extends Controller
 
     /**
      * Show the form for editing the specified page.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return Response
      */
     public function edit($bookSlug, $pageSlug)
@@ -185,8 +188,8 @@ class PageController extends Controller
     /**
      * Update the specified page in storage.
      * @param  Request $request
-     * @param          $bookSlug
-     * @param          $pageSlug
+     * @param  string $bookSlug
+     * @param  string $pageSlug
      * @return Response
      */
     public function update(Request $request, $bookSlug, $pageSlug)
@@ -205,7 +208,7 @@ class PageController extends Controller
     /**
      * Save a draft update as a revision.
      * @param Request $request
-     * @param $pageId
+     * @param int $pageId
      * @return \Illuminate\Http\JsonResponse
      */
     public function saveDraft(Request $request, $pageId)
@@ -230,7 +233,7 @@ class PageController extends Controller
     /**
      * Redirect from a special link url which
      * uses the page id rather than the name.
-     * @param $pageId
+     * @param int $pageId
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
     public function redirectFromLink($pageId)
@@ -241,8 +244,8 @@ class PageController extends Controller
 
     /**
      * Show the deletion page for the specified page.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\View\View
      */
     public function showDelete($bookSlug, $pageSlug)
@@ -257,8 +260,8 @@ class PageController extends Controller
 
     /**
      * Show the deletion page for the specified page.
-     * @param $bookSlug
-     * @param $pageId
+     * @param string $bookSlug
+     * @param int $pageId
      * @return \Illuminate\View\View
      * @throws NotFoundException
      */
@@ -273,8 +276,8 @@ class PageController extends Controller
 
     /**
      * Remove the specified page from storage.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return Response
      * @internal param int $id
      */
@@ -291,8 +294,8 @@ class PageController extends Controller
 
     /**
      * Remove the specified draft page from storage.
-     * @param $bookSlug
-     * @param $pageId
+     * @param string $bookSlug
+     * @param int $pageId
      * @return Response
      * @throws NotFoundException
      */
@@ -308,8 +311,8 @@ class PageController extends Controller
 
     /**
      * Shows the last revisions for this page.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\View\View
      */
     public function showRevisions($bookSlug, $pageSlug)
@@ -322,9 +325,9 @@ class PageController extends Controller
 
     /**
      * Shows a preview of a single revision
-     * @param $bookSlug
-     * @param $pageSlug
-     * @param $revisionId
+     * @param string $bookSlug
+     * @param string $pageSlug
+     * @param int $revisionId
      * @return \Illuminate\View\View
      */
     public function showRevision($bookSlug, $pageSlug, $revisionId)
@@ -339,9 +342,9 @@ class PageController extends Controller
 
     /**
      * Restores a page using the content of the specified revision.
-     * @param $bookSlug
-     * @param $pageSlug
-     * @param $revisionId
+     * @param string $bookSlug
+     * @param string $pageSlug
+     * @param int $revisionId
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
     public function restoreRevision($bookSlug, $pageSlug, $revisionId)
@@ -357,8 +360,8 @@ class PageController extends Controller
     /**
      * Exports a page to pdf format using barryvdh/laravel-dompdf wrapper.
      * https://github.com/barryvdh/laravel-dompdf
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\Http\Response
      */
     public function exportPdf($bookSlug, $pageSlug)
@@ -374,8 +377,8 @@ class PageController extends Controller
 
     /**
      * Export a page to a self-contained HTML file.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\Http\Response
      */
     public function exportHtml($bookSlug, $pageSlug)
@@ -391,8 +394,8 @@ class PageController extends Controller
 
     /**
      * Export a page to a simple plaintext .txt file.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\Http\Response
      */
     public function exportPlainText($bookSlug, $pageSlug)
@@ -434,8 +437,8 @@ class PageController extends Controller
 
     /**
      * Show the Restrictions view.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
     public function showRestrict($bookSlug, $pageSlug)
@@ -452,8 +455,8 @@ class PageController extends Controller
 
     /**
      * Show the view to choose a new parent to move a page into.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @return mixed
      * @throws NotFoundException
      */
@@ -470,8 +473,8 @@ class PageController extends Controller
 
     /**
      * Does the action of moving the location of a page
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @param Request $request
      * @return mixed
      * @throws NotFoundException
@@ -513,8 +516,8 @@ class PageController extends Controller
 
     /**
      * Set the permissions for this page.
-     * @param $bookSlug
-     * @param $pageSlug
+     * @param string $bookSlug
+     * @param string $pageSlug
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
index 7255ad05f64e06f24154809cca8fef05c28efafb..c64da126774bd3db3180028b8cb4f0a288dcee81 100644 (file)
@@ -7,6 +7,7 @@ use BookStack\Entity;
 use BookStack\Exceptions\NotFoundException;
 use Carbon\Carbon;
 use DOMDocument;
+use DOMXPath;
 use Illuminate\Support\Str;
 use BookStack\Page;
 use BookStack\PageRevision;
@@ -158,6 +159,35 @@ class PageRepo extends EntityRepo
         return $page;
     }
 
+    /**
+     * Parse te headers on the page to get a navigation menu
+     * @param Page $page
+     * @return array
+     */
+    public function getPageNav(Page $page)
+    {
+        if ($page->html == '') return null;
+        libxml_use_internal_errors(true);
+        $doc = new DOMDocument();
+        $doc->loadHTML(mb_convert_encoding($page->html, 'HTML-ENTITIES', 'UTF-8'));
+        $xPath = new DOMXPath($doc);
+        $headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6");
+
+        if (is_null($headers)) return null;
+
+        $tree = [];
+        foreach ($headers as $header) {
+            $text = $header->nodeValue;
+            $tree[] = [
+                'nodeName' => strtolower($header->nodeName),
+                'level' => intval(str_replace('h', '', $header->nodeName)),
+                'link' => '#' . $header->getAttribute('id'),
+                'text' => strlen($text) > 30 ? substr($text, 0, 27) . '...' : $text
+            ];
+        }
+        return $tree;
+    }
+
     /**
      * Formats a page's html to be tagged correctly
      * within the system.
index 755d558e83ffcde90d3f349e6568fc5da163f3c2..c1e6a92df26941dc683b92e3990f38aabb8a2bf0 100644 (file)
@@ -81,9 +81,10 @@ var mceOptions = module.exports = {
     toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image-insert link hr | removeformat code fullscreen",
     content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
     style_formats: [
-        {title: "Header 1", format: "h1"},
-        {title: "Header 2", format: "h2"},
-        {title: "Header 3", format: "h3"},
+        {title: "Header Large", format: "h2"},
+        {title: "Header Medium", format: "h3"},
+        {title: "Header Small", format: "h4"},
+        {title: "Header Tiny", format: "h5"},
         {title: "Paragraph", format: "p", exact: true, classes: ''},
         {title: "Blockquote", format: "blockquote"},
         {title: "Code Block", icon: "code", format: "pre"},
index 2658c46891bf75ebc99ec9553897be9e93bfa60b..54fd55dff6f075637a2d943a431e05a05a6458fc 100644 (file)
@@ -1,5 +1,5 @@
 .page-list {
-  h3 {
+  h4 {
     margin: $-l 0 $-xs 0;
     font-size: 1.666em;
   }
     overflow: hidden;
     margin-bottom: $-l;
   }
-  h4 {
+  h5 {
     display: block;
     margin: $-s 0 0 0;
     border-left: 5px solid $color-page;
     padding: $-xs 0 $-xs $-m;
+    font-size: 1.1em;
+    font-weight: normal;
     &.draft {
       border-left-color: $color-page-draft;
     }
   }
 }
 
-.page-nav-list {
+.sidebar-page-nav {
   $nav-indent: $-s;
-  margin-left: 2px;
   list-style: none;
+  margin: $-s 0 $-m 2px;
+  border-left: 2px dotted #BBB;
   li {
-    //border-left: 1px solid rgba(0, 0, 0, 0.1);
-    padding-left: $-xs;
-    border-left: 2px solid #888;
+    padding-left: $-s;
     margin-bottom: 4px;
+    font-size: 0.95em;
   }
-  li a {
-    color: #555;
+  .h1 {
+    margin-left: -2px;
   }
-  .nav-H2 {
+  .h2 {
+    margin-left: -2px;
+  }
+  .h3 {
     margin-left: $nav-indent;
-    font-size: 0.95em;
   }
-  .nav-H3 {
+  .h4 {
     margin-left: $nav-indent*2;
-    font-size: 0.90em
   }
-  .nav-H4 {
+  .h5 {
     margin-left: $nav-indent*3;
-    font-size: 0.85em
   }
-  .nav-H5 {
+  .h6 {
     margin-left: $nav-indent*4;
-    font-size: 0.80em
-  }
-  .nav-H6 {
-    margin-left: $nav-indent*5;
-    font-size: 0.75em
   }
 }
 
 // Sidebar list
 .book-tree {
-  padding: $-l 0 0 0;
+  padding: $-xs 0 0 0;
   position: relative;
   right: 0;
   top: 0;
@@ -306,10 +303,10 @@ ul.pagination {
 }
 
 .entity-list {
-  >div {
+  > div {
     padding: $-m 0;
   }
-  h3 {
+  h4 {
     margin: 0;
   }
   p {
@@ -327,9 +324,10 @@ ul.pagination {
     color: $color-page-draft;
   }
 }
+
 .entity-list.compact {
   font-size: 0.6em;
-  h3, a {
+  h4, a {
     line-height: 1.2;
   }
   p {
index cd81bb4e21326d24e2589751438b0a8e510e217b..fd993b685402813132cb3a4c0ff50405bc20b153 100644 (file)
@@ -15,31 +15,41 @@ h2 {
   margin-bottom: 0.43137255em;
 }
 h3 {
-  font-size: 1.75em;
+  font-size: 2.333em;
   line-height: 1.571428572em;
   margin-top: 0.78571429em;
   margin-bottom: 0.43137255em;
 }
 h4 {
-  font-size: 1em;
+  font-size: 1.666em;
   line-height: 1.375em;
   margin-top: 0.78571429em;
   margin-bottom: 0.43137255em;
 }
 
-h1, h2, h3, h4 {
+h1, h2, h3, h4, h5, h6 {
   font-weight: 400;
   position: relative;
   display: block;
   color: #555;
   .subheader {
-    //display: block;
     font-size: 0.5em;
     line-height: 1em;
     color: lighten($text-dark, 32%);
   }
 }
 
+h5 {
+  font-size: 1.4em;
+}
+
+h5, h6 {
+  font-weight: 500;
+  line-height: 1.2em;
+  margin-top: 0.78571429em;
+  margin-bottom: 0.66em;
+}
+
 /*
  * Link styling
  */
index 2eefdfbf531c0a9917cc2606a344e32402863ffd..605841f7f0ae31ada7c4c9f39d5b96757b7e9e0b 100644 (file)
@@ -1,5 +1,5 @@
 <div class="book entity-list-item"  data-entity-type="book" data-entity-id="{{$book->id}}">
-    <h3 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h3>
+    <h4 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h4>
     @if(isset($book->searchSnippet))
         <p class="text-muted">{!! $book->searchSnippet !!}</p>
     @else
index 35d3a7589d927b313e8e8488c8ff2490cc82a289..f70e592448f00068e554a9332f5ae5a3faf7f903 100644 (file)
@@ -1,5 +1,5 @@
 <div class="chapter entity-list-item" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
-    <h3>
+    <h4>
         @if (isset($showPath) && $showPath)
             <a href="{{ $chapter->book->getUrl() }}" class="text-book">
                 <i class="zmdi zmdi-book"></i>{{ $chapter->book->name }}
@@ -9,7 +9,7 @@
         <a href="{{ $chapter->getUrl() }}" class="text-chapter entity-list-item-link">
             <i class="zmdi zmdi-collection-bookmark"></i><span class="entity-list-item-name">{{ $chapter->name }}</span>
         </a>
-    </h3>
+    </h4>
     @if(isset($chapter->searchSnippet))
         <p class="text-muted">{!! $chapter->searchSnippet !!}</p>
     @else
@@ -20,7 +20,7 @@
         <p class="text-muted chapter-toggle"><i class="zmdi zmdi-caret-right"></i> <i class="zmdi zmdi-file-text"></i> <span>{{ count($chapter->pages) }} Pages</span></p>
         <div class="inset-list">
             @foreach($chapter->pages as $page)
-                <h4 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h4>
+                <h5 class="@if($page->draft) draft @endif"><a href="{{ $page->getUrl() }}" class="text-page @if($page->draft) draft @endif"><i class="zmdi zmdi-file-text"></i>{{$page->name}}</a></h5>
             @endforeach
         </div>
     @endif
index 2529c39c7f77fb81b5b3cc082ade0b2ec422bd28..2fb4ac85597cca57cc0f26d6e86bde9a55efb881 100644 (file)
             <div class="col-sm-4">
                 <div id="recent-drafts">
                     @if(count($draftPages) > 0)
-                        <h3>My Recent Drafts</h3>
+                        <h4>My Recent Drafts</h4>
                         @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
                     @endif
                 </div>
                 @if($signedIn)
-                    <h3>My Recently Viewed</h3>
+                    <h4>My Recently Viewed</h4>
                 @else
-                    <h3>Recent Books</h3>
+                    <h4>Recent Books</h4>
                 @endif
                 @include('partials/entity-list', [
                 'entities' => $recents,
@@ -42,7 +42,7 @@
             </div>
 
             <div class="col-sm-4">
-                <h3><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">Recently Created Pages</a></h3>
+                <h4><a class="no-color" href="{{ baseUrl("/pages/recently-created") }}">Recently Created Pages</a></h4>
                 <div id="recently-created-pages">
                     @include('partials/entity-list', [
                     'entities' => $recentlyCreatedPages,
@@ -51,7 +51,7 @@
                     ])
                 </div>
 
-                <h3><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">Recently Updated Pages</a></h3>
+                <h4><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">Recently Updated Pages</a></h4>
                 <div id="recently-updated-pages">
                     @include('partials/entity-list', [
                     'entities' => $recentlyUpdatedPages,
@@ -62,7 +62,7 @@
             </div>
 
             <div class="col-sm-4" id="recent-activity">
-                <h3>Recent Activity</h3>
+                <h4>Recent Activity</h4>
                 @include('partials/activity-list', ['activity' => $activity])
             </div>
 
index 98243f6fa88ba63a2d09c20af659917025348186..7aa5d7933fb6661968fdb38aa12e0f1fea2259fe 100644 (file)
@@ -1,7 +1,7 @@
 <div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}">
-    <h3>
+    <h4>
         <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link"><i class="zmdi zmdi-file-text"></i><span class="entity-list-item-name">{{ $page->name }}</span></a>
-    </h3>
+    </h4>
 
     @if(isset($page->searchSnippet))
         <p class="text-muted">{!! $page->searchSnippet !!}</p>
index 2e6e35476d9773d568f1aff4a7325a09d47a1b6c..af85075a204981b84e2644d5f93ea6eec1a3046b 100644 (file)
                         @endif
                     </div>
                 @endif
-                @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree])
+
+                @include('pages/sidebar-tree-list', ['book' => $book, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav])
             </div>
 
         </div>
index e40fdbf0fcaa28d673aeea356246a05894ea6fc3..5fcec8731ee098620366f836c346551a35530c2c 100644 (file)
@@ -1,5 +1,19 @@
 
 <div class="book-tree" ng-non-bindable>
+
+    @if (isset($pageNav) && $pageNav)
+        <h6 class="text-muted">Page Navigation</h6>
+        <div class="sidebar-page-nav menu">
+            @foreach($pageNav as $navItem)
+                <li class="page-nav-item {{ $navItem['nodeName'] }}">
+                    <a href="{{ $navItem['link'] }}">{{ $navItem['text'] }}</a>
+                </li>
+            @endforeach
+        </div>
+
+
+    @endif
+
     <h6 class="text-muted">Book Navigation</h6>
     <ul class="sidebar-page-list menu">
         <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>