/**
* Show the form for creating a new page.
- * @param $bookSlug
+ * @param string $bookSlug
* @param bool $chapterSlug
* @return Response
* @internal param bool $pageSlug
/**
* 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)
* 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)
$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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* Show the deletion page for the specified page.
- * @param $bookSlug
- * @param $pageId
+ * @param string $bookSlug
+ * @param int $pageId
* @return \Illuminate\View\View
* @throws NotFoundException
*/
/**
* Remove the specified page from storage.
- * @param $bookSlug
- * @param $pageSlug
+ * @param string $bookSlug
+ * @param string $pageSlug
* @return Response
* @internal param int $id
*/
/**
* Remove the specified draft page from storage.
- * @param $bookSlug
- * @param $pageId
+ * @param string $bookSlug
+ * @param int $pageId
* @return Response
* @throws NotFoundException
*/
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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)
/**
* 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
*/
/**
* 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
/**
* 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
*/
use BookStack\Exceptions\NotFoundException;
use Carbon\Carbon;
use DOMDocument;
+use DOMXPath;
use Illuminate\Support\Str;
use BookStack\Page;
use BookStack\PageRevision;
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.
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"},
.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;
}
.entity-list {
- >div {
+ > div {
padding: $-m 0;
}
- h3 {
+ h4 {
margin: 0;
}
p {
color: $color-page-draft;
}
}
+
.entity-list.compact {
font-size: 0.6em;
- h3, a {
+ h4, a {
line-height: 1.2;
}
p {
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
*/
<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
<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 }}
<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
<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
<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,
</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,
])
</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,
</div>
<div class="col-sm-4" id="recent-activity">
- <h3>Recent Activity</h3>
+ <h4>Recent Activity</h4>
@include('partials/activity-list', ['activity' => $activity])
</div>
<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>
@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>
<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>