Showing
13 changed files
with
220 additions
and
18 deletions
| ... | @@ -78,7 +78,9 @@ class BookController extends Controller | ... | @@ -78,7 +78,9 @@ class BookController extends Controller |
| 78 | public function show($slug) | 78 | public function show($slug) |
| 79 | { | 79 | { |
| 80 | $book = $this->bookRepo->getBySlug($slug); | 80 | $book = $this->bookRepo->getBySlug($slug); |
| 81 | - return view('books/show', ['book' => $book]); | 81 | + $pageTree = $this->pageRepo->getTreeByBookId($book->id); |
| 82 | + // dd($pageTree); | ||
| 83 | + return view('books/show', ['book' => $book, 'pageTree' => $pageTree]); | ||
| 82 | } | 84 | } |
| 83 | 85 | ||
| 84 | /** | 86 | /** | ... | ... |
| ... | @@ -41,12 +41,14 @@ class PageController extends Controller | ... | @@ -41,12 +41,14 @@ class PageController extends Controller |
| 41 | * Show the form for creating a new resource. | 41 | * Show the form for creating a new resource. |
| 42 | * | 42 | * |
| 43 | * @param $bookSlug | 43 | * @param $bookSlug |
| 44 | + * @param bool $pageSlug | ||
| 44 | * @return Response | 45 | * @return Response |
| 45 | */ | 46 | */ |
| 46 | - public function create($bookSlug) | 47 | + public function create($bookSlug, $pageSlug = false) |
| 47 | { | 48 | { |
| 48 | $book = $this->bookRepo->getBySlug($bookSlug); | 49 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 49 | - return view('pages/create', ['book' => $book]); | 50 | + $page = $pageSlug ? $this->pageRepo->getBySlug($pageSlug, $book->id) : false; |
| 51 | + return view('pages/create', ['book' => $book, 'parentPage' => $page]); | ||
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | /** | 54 | /** |
| ... | @@ -61,7 +63,8 @@ class PageController extends Controller | ... | @@ -61,7 +63,8 @@ class PageController extends Controller |
| 61 | $this->validate($request, [ | 63 | $this->validate($request, [ |
| 62 | 'name' => 'required|string|max:255', | 64 | 'name' => 'required|string|max:255', |
| 63 | 'html' => 'required|string', | 65 | 'html' => 'required|string', |
| 64 | - 'priority' => 'integer' | 66 | + 'priority' => 'integer', |
| 67 | + 'parent' => 'integer|exists:pages,id' | ||
| 65 | ]); | 68 | ]); |
| 66 | $book = $this->bookRepo->getBySlug($bookSlug); | 69 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 67 | $page = $this->pageRepo->newFromInput($request->all()); | 70 | $page = $this->pageRepo->newFromInput($request->all()); |
| ... | @@ -70,6 +73,11 @@ class PageController extends Controller | ... | @@ -70,6 +73,11 @@ class PageController extends Controller |
| 70 | $slug .= '1'; | 73 | $slug .= '1'; |
| 71 | } | 74 | } |
| 72 | $page->slug =$slug; | 75 | $page->slug =$slug; |
| 76 | + | ||
| 77 | + if($request->has('parent')) { | ||
| 78 | + $page->page_id = $request->get('parent'); | ||
| 79 | + } | ||
| 80 | + | ||
| 73 | $page->book_id = $book->id; | 81 | $page->book_id = $book->id; |
| 74 | $page->text = strip_tags($page->html); | 82 | $page->text = strip_tags($page->html); |
| 75 | $page->save(); | 83 | $page->save(); |
| ... | @@ -87,7 +95,8 @@ class PageController extends Controller | ... | @@ -87,7 +95,8 @@ class PageController extends Controller |
| 87 | { | 95 | { |
| 88 | $book = $this->bookRepo->getBySlug($bookSlug); | 96 | $book = $this->bookRepo->getBySlug($bookSlug); |
| 89 | $page = $this->pageRepo->getBySlug($pageSlug, $book->id); | 97 | $page = $this->pageRepo->getBySlug($pageSlug, $book->id); |
| 90 | - return view('pages/show', ['page' => $page]); | 98 | + $breadCrumbs = $this->pageRepo->getBreadCrumbs($page); |
| 99 | + return view('pages/show', ['page' => $page, 'breadCrumbs' => $breadCrumbs, 'book' => $book]); | ||
| 91 | } | 100 | } |
| 92 | 101 | ||
| 93 | /** | 102 | /** | ... | ... |
| ... | @@ -25,6 +25,7 @@ Route::group(['prefix' => 'books'], function() { | ... | @@ -25,6 +25,7 @@ Route::group(['prefix' => 'books'], function() { |
| 25 | Route::get('/{bookSlug}/page/create', 'PageController@create'); | 25 | Route::get('/{bookSlug}/page/create', 'PageController@create'); |
| 26 | Route::post('/{bookSlug}/page', 'PageController@store'); | 26 | Route::post('/{bookSlug}/page', 'PageController@store'); |
| 27 | Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); | 27 | Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); |
| 28 | + Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create'); | ||
| 28 | Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); | 29 | Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); |
| 29 | Route::put('/{bookSlug}/{pageSlug}', 'PageController@update'); | 30 | Route::put('/{bookSlug}/{pageSlug}', 'PageController@update'); |
| 30 | }); | 31 | }); | ... | ... |
| ... | @@ -8,13 +8,33 @@ class Page extends Model | ... | @@ -8,13 +8,33 @@ class Page extends Model |
| 8 | { | 8 | { |
| 9 | protected $fillable = ['name', 'html', 'priority']; | 9 | protected $fillable = ['name', 'html', 'priority']; |
| 10 | 10 | ||
| 11 | + protected $simpleAttributes = ['name', 'id', 'slug']; | ||
| 12 | + | ||
| 13 | + public function toSimpleArray() | ||
| 14 | + { | ||
| 15 | + $array = array_intersect_key($this->toArray(), array_flip($this->simpleAttributes)); | ||
| 16 | + $array['url'] = $this->getUrl(); | ||
| 17 | + return $array; | ||
| 18 | + } | ||
| 19 | + | ||
| 11 | public function book() | 20 | public function book() |
| 12 | { | 21 | { |
| 13 | return $this->belongsTo('Oxbow\Book'); | 22 | return $this->belongsTo('Oxbow\Book'); |
| 14 | } | 23 | } |
| 15 | 24 | ||
| 25 | + public function children() | ||
| 26 | + { | ||
| 27 | + return $this->hasMany('Oxbow\Page'); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public function parent() | ||
| 31 | + { | ||
| 32 | + return $this->belongsTo('Oxbow\Page', 'page_id'); | ||
| 33 | + } | ||
| 34 | + | ||
| 16 | public function getUrl() | 35 | public function getUrl() |
| 17 | { | 36 | { |
| 18 | return '/books/' . $this->book->slug . '/' . $this->slug; | 37 | return '/books/' . $this->book->slug . '/' . $this->slug; |
| 19 | } | 38 | } |
| 39 | + | ||
| 20 | } | 40 | } | ... | ... |
| ... | @@ -53,4 +53,11 @@ class BookRepo | ... | @@ -53,4 +53,11 @@ class BookRepo |
| 53 | $book->delete(); | 53 | $book->delete(); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | + public function getTree($book) | ||
| 57 | + { | ||
| 58 | + $tree = $book->toArray(); | ||
| 59 | + $tree['pages'] = $this->pageRepo->getTreeByBookId($book->id); | ||
| 60 | + return $tree; | ||
| 61 | + } | ||
| 62 | + | ||
| 56 | } | 63 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -59,4 +59,62 @@ class PageRepo | ... | @@ -59,4 +59,62 @@ class PageRepo |
| 59 | return $query->get(); | 59 | return $query->get(); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | + public function getBreadCrumbs($page) | ||
| 63 | + { | ||
| 64 | + $tree = []; | ||
| 65 | + $cPage = $page; | ||
| 66 | + while($cPage->parent && $cPage->parent->id !== 0) { | ||
| 67 | + $cPage = $cPage->parent; | ||
| 68 | + $tree[] = $cPage; | ||
| 69 | + } | ||
| 70 | + return count($tree) > 0 ? array_reverse($tree) : false; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * Creates a tree of child pages, Nested by their | ||
| 75 | + * set parent pages. | ||
| 76 | + * @param $bookId | ||
| 77 | + * @param bool $currentPageId | ||
| 78 | + * @return array | ||
| 79 | + */ | ||
| 80 | + public function getTreeByBookId($bookId, $currentPageId = false) | ||
| 81 | + { | ||
| 82 | + $topLevelPages = $this->getTopLevelPages($bookId); | ||
| 83 | + $pageTree = []; | ||
| 84 | + | ||
| 85 | + foreach($topLevelPages as $key => $topPage) { | ||
| 86 | + $pageTree[$key] = $this->toArrayTree($topPage, $currentPageId); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + return $pageTree; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * Creates a page tree array with the supplied page | ||
| 94 | + * as the parent of the tree. | ||
| 95 | + * @param $page | ||
| 96 | + * @param bool $currentPageId | ||
| 97 | + * @return mixed | ||
| 98 | + */ | ||
| 99 | + private function toArrayTree($page, $currentPageId = false) | ||
| 100 | + { | ||
| 101 | + $cPage = $page->toSimpleArray(); | ||
| 102 | + $cPage['current'] = ($currentPageId !== false && $cPage['id'] === $currentPageId); | ||
| 103 | + $cPage['pages'] = []; | ||
| 104 | + foreach($page->children as $key => $childPage) { | ||
| 105 | + $cPage['pages'][$key] = $this->toArrayTree($childPage); | ||
| 106 | + } | ||
| 107 | + $cPage['hasChildren'] = count($cPage['pages']) > 0; | ||
| 108 | + return $cPage; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * Gets the pages at the top of the page hierarchy. | ||
| 113 | + * @param $bookId | ||
| 114 | + */ | ||
| 115 | + private function getTopLevelPages($bookId) | ||
| 116 | + { | ||
| 117 | + return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->get(); | ||
| 118 | + } | ||
| 119 | + | ||
| 62 | } | 120 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -72,6 +72,9 @@ header .menu { | ... | @@ -72,6 +72,9 @@ header .menu { |
| 72 | &.left { | 72 | &.left { |
| 73 | float: left; | 73 | float: left; |
| 74 | } | 74 | } |
| 75 | + h1 { | ||
| 76 | + margin-top: 0.2em; | ||
| 77 | + } | ||
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | .page-list { | 80 | .page-list { |
| ... | @@ -205,3 +208,64 @@ h1, h2, h3, h4, h5, h6 { | ... | @@ -205,3 +208,64 @@ h1, h2, h3, h4, h5, h6 { |
| 205 | opacity: 1; | 208 | opacity: 1; |
| 206 | } | 209 | } |
| 207 | } | 210 | } |
| 211 | + | ||
| 212 | +.breadcrumbs { | ||
| 213 | + margin-top: $-s; | ||
| 214 | + a, span { | ||
| 215 | + color: #666; | ||
| 216 | + font-size: 0.9em; | ||
| 217 | + } | ||
| 218 | + i { | ||
| 219 | + padding-right: 4px; | ||
| 220 | + } | ||
| 221 | + span.sep { | ||
| 222 | + color: #888; | ||
| 223 | + padding: 0 $-xs; | ||
| 224 | + } | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | + | ||
| 228 | +.nested-page-list { | ||
| 229 | + list-style: none; | ||
| 230 | + margin-left: 0; | ||
| 231 | + li { | ||
| 232 | + border-top: 3px dotted #BBB; | ||
| 233 | + padding: $-s 0; | ||
| 234 | + user-select: none; | ||
| 235 | + } | ||
| 236 | + li:last-child { | ||
| 237 | + border-bottom: 3px dotted #BBB; | ||
| 238 | + } | ||
| 239 | + .nested-page-list { | ||
| 240 | + margin-top: $-xs; | ||
| 241 | + display: none; | ||
| 242 | + margin: $-xs 0 $-xs 9px; | ||
| 243 | + font-size: $fs-m * 0.9; | ||
| 244 | + border-left: 2px solid #EEE; | ||
| 245 | + } | ||
| 246 | + .nested-page-list li { | ||
| 247 | + border: none; | ||
| 248 | + padding-right: $-m; | ||
| 249 | + padding-left: $-m; | ||
| 250 | + &.expanded.has-children { | ||
| 251 | + padding-bottom: 0; | ||
| 252 | + } | ||
| 253 | + } | ||
| 254 | + i.arrow { | ||
| 255 | + font-size: 0.8em; | ||
| 256 | + padding: $-xs; | ||
| 257 | + margin-top: -$-xs; | ||
| 258 | + margin-bottom: -$-xs; | ||
| 259 | + transform-origin: 50% 50%; | ||
| 260 | + transition: transform ease-in-out 180ms; | ||
| 261 | + cursor: pointer; | ||
| 262 | + } | ||
| 263 | + li.expanded { | ||
| 264 | + > i.arrow { | ||
| 265 | + transform: rotate(90deg); | ||
| 266 | + } | ||
| 267 | + >.nested-page-list { | ||
| 268 | + display: block; | ||
| 269 | + } | ||
| 270 | + } | ||
| 271 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -20,24 +20,24 @@ | ... | @@ -20,24 +20,24 @@ |
| 20 | <h4 class="float">Pages</h4> | 20 | <h4 class="float">Pages</h4> |
| 21 | <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos float right">+ New Page</a> | 21 | <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos float right">+ New Page</a> |
| 22 | </div> | 22 | </div> |
| 23 | - <div class="page-list"> | ||
| 24 | - @if(count($book->pages) > 0) | ||
| 25 | - @foreach($book->pages as $page) | ||
| 26 | - <a href="{{$page->getUrl()}}">{{$page->name}}</a> | ||
| 27 | - @endforeach | ||
| 28 | - @else | ||
| 29 | - <p class="text-muted">This book has no pages</p> | ||
| 30 | - @endif | ||
| 31 | - </div> | ||
| 32 | 23 | ||
| 33 | - <p> | 24 | + @include('pages/page-tree-list', ['pageTree' => $pageTree]) |
| 34 | 25 | ||
| 35 | - </p> | 26 | + </div> |
| 36 | 27 | ||
| 37 | 28 | ||
| 38 | </div> | 29 | </div> |
| 39 | 30 | ||
| 31 | + <script> | ||
| 32 | + $(function() { | ||
| 40 | 33 | ||
| 41 | - </div> | 34 | + $('.nested-page-list i.arrow').click(function() { |
| 35 | + var list = $(this).closest('.nested-page-list'); | ||
| 36 | + var listItem = $(this).closest('li'); | ||
| 37 | + listItem.toggleClass('expanded'); | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + }); | ||
| 41 | + </script> | ||
| 42 | 42 | ||
| 43 | @stop | 43 | @stop |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -9,6 +9,9 @@ | ... | @@ -9,6 +9,9 @@ |
| 9 | @section('content') | 9 | @section('content') |
| 10 | <form action="{{$book->getUrl() . '/page'}}" method="POST"> | 10 | <form action="{{$book->getUrl() . '/page'}}" method="POST"> |
| 11 | @include('pages/form') | 11 | @include('pages/form') |
| 12 | + @if($parentPage) | ||
| 13 | + <input type="hidden" name="parent" value="{{$parentPage->id}}"> | ||
| 14 | + @endif | ||
| 12 | </form> | 15 | </form> |
| 13 | @stop | 16 | @stop |
| 14 | 17 | ... | ... |
| 1 | + | ||
| 2 | + | ||
| 3 | +<ul class="nested-page-list"> | ||
| 4 | + @foreach($pageTree as $subPage) | ||
| 5 | + <li @if($subPage['hasChildren'])class="has-children"@endif> | ||
| 6 | + @if($subPage['hasChildren']) | ||
| 7 | + <i class="fa fa-chevron-right arrow"></i> | ||
| 8 | + @endif | ||
| 9 | + <a href="{{$subPage['url']}}">{{$subPage['name']}}</a> | ||
| 10 | + @if($subPage['hasChildren']) | ||
| 11 | + @include('pages/page-tree-list', ['pageTree' => $subPage['pages']]) | ||
| 12 | + @endif | ||
| 13 | + </li> | ||
| 14 | + @endforeach | ||
| 15 | +</ul> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -10,12 +10,32 @@ | ... | @@ -10,12 +10,32 @@ |
| 10 | </div> | 10 | </div> |
| 11 | <div class="page-actions"> | 11 | <div class="page-actions"> |
| 12 | <h4>Actions</h4> | 12 | <h4>Actions</h4> |
| 13 | + <div class="list"> | ||
| 13 | <a href="{{$page->getUrl() . '/edit'}}" class="muted"><i class="fa fa-pencil"></i>Edit this page</a> | 14 | <a href="{{$page->getUrl() . '/edit'}}" class="muted"><i class="fa fa-pencil"></i>Edit this page</a> |
| 15 | + <a href="{{$page->getUrl() . '/create'}}" class="muted"><i class="fa fa-file-o"></i>Create Sub-page</a> | ||
| 16 | + </div> | ||
| 14 | </div> | 17 | </div> |
| 15 | </div> | 18 | </div> |
| 16 | 19 | ||
| 17 | <div class="page-content right col-md-9"> | 20 | <div class="page-content right col-md-9"> |
| 21 | + <div class="breadcrumbs"> | ||
| 22 | + <a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{ $book->name }}</a> | ||
| 23 | + @if($breadCrumbs) | ||
| 24 | + @foreach($breadCrumbs as $parentPage) | ||
| 25 | + <span class="sep">></span> | ||
| 26 | + <a href="{{$parentPage->getUrl()}}">{{ $parentPage->name }}</a> | ||
| 27 | + @endforeach | ||
| 28 | + @endif | ||
| 29 | + </div> | ||
| 18 | <h1>{{$page->name}}</h1> | 30 | <h1>{{$page->name}}</h1> |
| 31 | + @if(count($page->pages) > 0) | ||
| 32 | + <h4 class="text-muted">Sub-pages</h4> | ||
| 33 | + <div class="page-list"> | ||
| 34 | + @foreach($page->pages as $childPage) | ||
| 35 | + <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a> | ||
| 36 | + @endforeach | ||
| 37 | + </div> | ||
| 38 | + @endif | ||
| 19 | {!! $page->html !!} | 39 | {!! $page->html !!} |
| 20 | </div> | 40 | </div> |
| 21 | </div> | 41 | </div> |
| ... | @@ -37,7 +57,6 @@ | ... | @@ -37,7 +57,6 @@ |
| 37 | var pageNav = $('.page-nav-list'); | 57 | var pageNav = $('.page-nav-list'); |
| 38 | var pageContent = $('.page-content'); | 58 | var pageContent = $('.page-content'); |
| 39 | var headers = pageContent.find('h1, h2, h3, h4, h5, h6'); | 59 | var headers = pageContent.find('h1, h2, h3, h4, h5, h6'); |
| 40 | - var sortedHeaders = []; | ||
| 41 | headers.each(function() { | 60 | headers.each(function() { |
| 42 | var header = $(this); | 61 | var header = $(this); |
| 43 | var tag = header.prop('tagName'); | 62 | var tag = header.prop('tagName'); | ... | ... |
-
Please register or sign in to post a comment