diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index c17a082a5..03954fc51 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -78,7 +78,9 @@ class BookController extends Controller public function show($slug) { $book = $this->bookRepo->getBySlug($slug); - return view('books/show', ['book' => $book]); + $pageTree = $this->pageRepo->getTreeByBookId($book->id); + // dd($pageTree); + return view('books/show', ['book' => $book, 'pageTree' => $pageTree]); } /** diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 57901573b..f2a137429 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -41,12 +41,14 @@ class PageController extends Controller * Show the form for creating a new resource. * * @param $bookSlug + * @param bool $pageSlug * @return Response */ - public function create($bookSlug) + public function create($bookSlug, $pageSlug = false) { $book = $this->bookRepo->getBySlug($bookSlug); - return view('pages/create', ['book' => $book]); + $page = $pageSlug ? $this->pageRepo->getBySlug($pageSlug, $book->id) : false; + return view('pages/create', ['book' => $book, 'parentPage' => $page]); } /** @@ -61,7 +63,8 @@ class PageController extends Controller $this->validate($request, [ 'name' => 'required|string|max:255', 'html' => 'required|string', - 'priority' => 'integer' + 'priority' => 'integer', + 'parent' => 'integer|exists:pages,id' ]); $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->newFromInput($request->all()); @@ -70,6 +73,11 @@ class PageController extends Controller $slug .= '1'; } $page->slug =$slug; + + if($request->has('parent')) { + $page->page_id = $request->get('parent'); + } + $page->book_id = $book->id; $page->text = strip_tags($page->html); $page->save(); @@ -87,7 +95,8 @@ class PageController extends Controller { $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->getBySlug($pageSlug, $book->id); - return view('pages/show', ['page' => $page]); + $breadCrumbs = $this->pageRepo->getBreadCrumbs($page); + return view('pages/show', ['page' => $page, 'breadCrumbs' => $breadCrumbs, 'book' => $book]); } /** diff --git a/app/Http/routes.php b/app/Http/routes.php index c190da616..e80f53274 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -25,6 +25,7 @@ Route::group(['prefix' => 'books'], function() { Route::get('/{bookSlug}/page/create', 'PageController@create'); Route::post('/{bookSlug}/page', 'PageController@store'); Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); + Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create'); Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); Route::put('/{bookSlug}/{pageSlug}', 'PageController@update'); }); diff --git a/app/Page.php b/app/Page.php index d14f71520..3d9e6e3b2 100644 --- a/app/Page.php +++ b/app/Page.php @@ -8,13 +8,33 @@ class Page extends Model { protected $fillable = ['name', 'html', 'priority']; + protected $simpleAttributes = ['name', 'id', 'slug']; + + public function toSimpleArray() + { + $array = array_intersect_key($this->toArray(), array_flip($this->simpleAttributes)); + $array['url'] = $this->getUrl(); + return $array; + } + public function book() { return $this->belongsTo('Oxbow\Book'); } + public function children() + { + return $this->hasMany('Oxbow\Page'); + } + + public function parent() + { + return $this->belongsTo('Oxbow\Page', 'page_id'); + } + public function getUrl() { return '/books/' . $this->book->slug . '/' . $this->slug; } + } diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php index f48fa9944..3693fe40a 100644 --- a/app/Repos/BookRepo.php +++ b/app/Repos/BookRepo.php @@ -53,4 +53,11 @@ class BookRepo $book->delete(); } + public function getTree($book) + { + $tree = $book->toArray(); + $tree['pages'] = $this->pageRepo->getTreeByBookId($book->id); + return $tree; + } + } \ No newline at end of file diff --git a/app/Repos/PageRepo.php b/app/Repos/PageRepo.php index a62907472..08da6654b 100644 --- a/app/Repos/PageRepo.php +++ b/app/Repos/PageRepo.php @@ -59,4 +59,62 @@ class PageRepo return $query->get(); } + public function getBreadCrumbs($page) + { + $tree = []; + $cPage = $page; + while($cPage->parent && $cPage->parent->id !== 0) { + $cPage = $cPage->parent; + $tree[] = $cPage; + } + return count($tree) > 0 ? array_reverse($tree) : false; + } + + /** + * Creates a tree of child pages, Nested by their + * set parent pages. + * @param $bookId + * @param bool $currentPageId + * @return array + */ + public function getTreeByBookId($bookId, $currentPageId = false) + { + $topLevelPages = $this->getTopLevelPages($bookId); + $pageTree = []; + + foreach($topLevelPages as $key => $topPage) { + $pageTree[$key] = $this->toArrayTree($topPage, $currentPageId); + } + + return $pageTree; + } + + /** + * Creates a page tree array with the supplied page + * as the parent of the tree. + * @param $page + * @param bool $currentPageId + * @return mixed + */ + private function toArrayTree($page, $currentPageId = false) + { + $cPage = $page->toSimpleArray(); + $cPage['current'] = ($currentPageId !== false && $cPage['id'] === $currentPageId); + $cPage['pages'] = []; + foreach($page->children as $key => $childPage) { + $cPage['pages'][$key] = $this->toArrayTree($childPage); + } + $cPage['hasChildren'] = count($cPage['pages']) > 0; + return $cPage; + } + + /** + * Gets the pages at the top of the page hierarchy. + * @param $bookId + */ + private function getTopLevelPages($bookId) + { + return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->get(); + } + } \ No newline at end of file diff --git a/resources/assets/sass/_text.scss b/resources/assets/sass/_text.scss index 7c8e025dd..d083a9070 100644 --- a/resources/assets/sass/_text.scss +++ b/resources/assets/sass/_text.scss @@ -199,4 +199,8 @@ ul { h1, h2, h3, h4, h5, h6 { margin: 0; } +} + +.list > * { + display: block; } \ No newline at end of file diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss index 17f702704..ef9a69bc3 100644 --- a/resources/assets/sass/styles.scss +++ b/resources/assets/sass/styles.scss @@ -72,6 +72,9 @@ header .menu { &.left { float: left; } + h1 { + margin-top: 0.2em; + } } .page-list { @@ -204,4 +207,65 @@ h1, h2, h3, h4, h5, h6 { &:hover a.link-hook { opacity: 1; } +} + +.breadcrumbs { + margin-top: $-s; + a, span { + color: #666; + font-size: 0.9em; + } + i { + padding-right: 4px; + } + span.sep { + color: #888; + padding: 0 $-xs; + } +} + + +.nested-page-list { + list-style: none; + margin-left: 0; + li { + border-top: 3px dotted #BBB; + padding: $-s 0; + user-select: none; + } + li:last-child { + border-bottom: 3px dotted #BBB; + } + .nested-page-list { + margin-top: $-xs; + display: none; + margin: $-xs 0 $-xs 9px; + font-size: $fs-m * 0.9; + border-left: 2px solid #EEE; + } + .nested-page-list li { + border: none; + padding-right: $-m; + padding-left: $-m; + &.expanded.has-children { + padding-bottom: 0; + } + } + i.arrow { + font-size: 0.8em; + padding: $-xs; + margin-top: -$-xs; + margin-bottom: -$-xs; + transform-origin: 50% 50%; + transition: transform ease-in-out 180ms; + cursor: pointer; + } + li.expanded { + > i.arrow { + transform: rotate(90deg); + } + >.nested-page-list { + display: block; + } + } } \ No newline at end of file diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index 4bc2f10f0..f562655f8 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -20,24 +20,24 @@ <h4 class="float">Pages</h4> <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos float right">+ New Page</a> </div> - <div class="page-list"> - @if(count($book->pages) > 0) - @foreach($book->pages as $page) - <a href="{{$page->getUrl()}}">{{$page->name}}</a> - @endforeach - @else - <p class="text-muted">This book has no pages</p> - @endif - </div> - - <p> - - </p> + @include('pages/page-tree-list', ['pageTree' => $pageTree]) </div> </div> + <script> + $(function() { + + $('.nested-page-list i.arrow').click(function() { + var list = $(this).closest('.nested-page-list'); + var listItem = $(this).closest('li'); + listItem.toggleClass('expanded'); + }); + + }); + </script> + @stop \ No newline at end of file diff --git a/resources/views/pages/create.blade.php b/resources/views/pages/create.blade.php index 8276f4d91..8876761ea 100644 --- a/resources/views/pages/create.blade.php +++ b/resources/views/pages/create.blade.php @@ -9,6 +9,9 @@ @section('content') <form action="{{$book->getUrl() . '/page'}}" method="POST"> @include('pages/form') + @if($parentPage) + <input type="hidden" name="parent" value="{{$parentPage->id}}"> + @endif </form> @stop diff --git a/resources/views/pages/form.blade.php b/resources/views/pages/form.blade.php index 7e0cd3eb0..871344f63 100644 --- a/resources/views/pages/form.blade.php +++ b/resources/views/pages/form.blade.php @@ -8,7 +8,7 @@ {{ csrf_field() }} <div class="title-input page-title"> - @include('form/text', ['name' => 'name', 'placeholder' => 'Enter Page Title']) + @include('form/text', ['name' => 'name', 'placeholder' => 'Enter Page Title']) </div> <div class="edit-area"> @include('form/textarea', ['name' => 'html']) diff --git a/resources/views/pages/page-tree-list.blade.php b/resources/views/pages/page-tree-list.blade.php new file mode 100644 index 000000000..9471bdfe6 --- /dev/null +++ b/resources/views/pages/page-tree-list.blade.php @@ -0,0 +1,15 @@ + + +<ul class="nested-page-list"> + @foreach($pageTree as $subPage) + <li @if($subPage['hasChildren'])class="has-children"@endif> + @if($subPage['hasChildren']) + <i class="fa fa-chevron-right arrow"></i> + @endif + <a href="{{$subPage['url']}}">{{$subPage['name']}}</a> + @if($subPage['hasChildren']) + @include('pages/page-tree-list', ['pageTree' => $subPage['pages']]) + @endif + </li> + @endforeach +</ul> \ No newline at end of file diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php index 0c0a6585e..0a957fc5e 100644 --- a/resources/views/pages/show.blade.php +++ b/resources/views/pages/show.blade.php @@ -10,12 +10,32 @@ </div> <div class="page-actions"> <h4>Actions</h4> - <a href="{{$page->getUrl() . '/edit'}}" class="muted"><i class="fa fa-pencil"></i>Edit this page</a> + <div class="list"> + <a href="{{$page->getUrl() . '/edit'}}" class="muted"><i class="fa fa-pencil"></i>Edit this page</a> + <a href="{{$page->getUrl() . '/create'}}" class="muted"><i class="fa fa-file-o"></i>Create Sub-page</a> + </div> </div> </div> <div class="page-content right col-md-9"> + <div class="breadcrumbs"> + <a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{ $book->name }}</a> + @if($breadCrumbs) + @foreach($breadCrumbs as $parentPage) + <span class="sep">></span> + <a href="{{$parentPage->getUrl()}}">{{ $parentPage->name }}</a> + @endforeach + @endif + </div> <h1>{{$page->name}}</h1> + @if(count($page->pages) > 0) + <h4 class="text-muted">Sub-pages</h4> + <div class="page-list"> + @foreach($page->pages as $childPage) + <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a> + @endforeach + </div> + @endif {!! $page->html !!} </div> </div> @@ -37,7 +57,6 @@ var pageNav = $('.page-nav-list'); var pageContent = $('.page-content'); var headers = pageContent.find('h1, h2, h3, h4, h5, h6'); - var sortedHeaders = []; headers.each(function() { var header = $(this); var tag = header.prop('tagName');