Dan Brown

Got the tree view working

...@@ -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
......
...@@ -200,3 +200,7 @@ ul { ...@@ -200,3 +200,7 @@ ul {
200 margin: 0; 200 margin: 0;
201 } 201 }
202 } 202 }
203 +
204 +.list > * {
205 + display: block;
206 +}
...\ 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">&gt;</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');
......