Dan Brown

Made chapters functional and cleaned design features

...@@ -31,7 +31,7 @@ class Book extends Model ...@@ -31,7 +31,7 @@ class Book extends Model
31 31
32 public function children() 32 public function children()
33 { 33 {
34 - $pages = $this->pages()->get(); 34 + $pages = $this->pages()->where('chapter_id', '=', 0)->get();
35 $chapters = $this->chapters()->get(); 35 $chapters = $this->chapters()->get();
36 $children = $pages->merge($chapters); 36 $children = $pages->merge($chapters);
37 return $children->sortBy('priority'); 37 return $children->sortBy('priority');
......
...@@ -12,7 +12,7 @@ class Chapter extends Model ...@@ -12,7 +12,7 @@ class Chapter extends Model
12 return $this->belongsTo('Oxbow\Book'); 12 return $this->belongsTo('Oxbow\Book');
13 } 13 }
14 14
15 - public function children() 15 + public function pages()
16 { 16 {
17 return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC'); 17 return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC');
18 } 18 }
...@@ -22,4 +22,9 @@ class Chapter extends Model ...@@ -22,4 +22,9 @@ class Chapter extends Model
22 return '/books/' . $this->book->slug . '/chapter/' . $this->slug; 22 return '/books/' . $this->book->slug . '/chapter/' . $this->slug;
23 } 23 }
24 24
25 + public function getExcerpt($length = 100)
26 + {
27 + return strlen($this->description) > $length ? substr($this->description, 0, $length-3) . '...' : $this->description;
28 + }
29 +
25 } 30 }
......
...@@ -7,6 +7,7 @@ use Illuminate\Http\Request; ...@@ -7,6 +7,7 @@ use Illuminate\Http\Request;
7 use Illuminate\Support\Str; 7 use Illuminate\Support\Str;
8 use Oxbow\Http\Requests; 8 use Oxbow\Http\Requests;
9 use Oxbow\Repos\BookRepo; 9 use Oxbow\Repos\BookRepo;
10 +use Oxbow\Repos\ChapterRepo;
10 use Oxbow\Repos\PageRepo; 11 use Oxbow\Repos\PageRepo;
11 12
12 class PageController extends Controller 13 class PageController extends Controller
...@@ -14,16 +15,19 @@ class PageController extends Controller ...@@ -14,16 +15,19 @@ class PageController extends Controller
14 15
15 protected $pageRepo; 16 protected $pageRepo;
16 protected $bookRepo; 17 protected $bookRepo;
18 + protected $chapterRepo;
17 19
18 /** 20 /**
19 * PageController constructor. 21 * PageController constructor.
20 - * @param $pageRepo 22 + * @param PageRepo $pageRepo
21 - * @param $bookRepo 23 + * @param BookRepo $bookRepo
24 + * @param ChapterRepo $chapterRepo
22 */ 25 */
23 - public function __construct(PageRepo $pageRepo, BookRepo $bookRepo) 26 + public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo)
24 { 27 {
25 $this->pageRepo = $pageRepo; 28 $this->pageRepo = $pageRepo;
26 $this->bookRepo = $bookRepo; 29 $this->bookRepo = $bookRepo;
30 + $this->chapterRepo = $chapterRepo;
27 } 31 }
28 32
29 33
...@@ -41,14 +45,15 @@ class PageController extends Controller ...@@ -41,14 +45,15 @@ class PageController extends Controller
41 * Show the form for creating a new resource. 45 * Show the form for creating a new resource.
42 * 46 *
43 * @param $bookSlug 47 * @param $bookSlug
44 - * @param bool $pageSlug 48 + * @param bool $chapterSlug
45 * @return Response 49 * @return Response
50 + * @internal param bool $pageSlug
46 */ 51 */
47 - public function create($bookSlug, $pageSlug = false) 52 + public function create($bookSlug, $chapterSlug = false)
48 { 53 {
49 $book = $this->bookRepo->getBySlug($bookSlug); 54 $book = $this->bookRepo->getBySlug($bookSlug);
50 - $page = $pageSlug ? $this->pageRepo->getBySlug($pageSlug, $book->id) : false; 55 + $chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : false;
51 - return view('pages/create', ['book' => $book, 'parentPage' => $page]); 56 + return view('pages/create', ['book' => $book, 'chapter' => $chapter]);
52 } 57 }
53 58
54 /** 59 /**
...@@ -67,15 +72,12 @@ class PageController extends Controller ...@@ -67,15 +72,12 @@ class PageController extends Controller
67 ]); 72 ]);
68 $book = $this->bookRepo->getBySlug($bookSlug); 73 $book = $this->bookRepo->getBySlug($bookSlug);
69 $page = $this->pageRepo->newFromInput($request->all()); 74 $page = $this->pageRepo->newFromInput($request->all());
70 - $slug = Str::slug($page->name); 75 +
71 - while($this->pageRepo->countBySlug($slug, $book->id) > 0) { 76 + $page->slug = $this->pageRepo->findSuitableSlug($page->name, $book->id);
72 - $slug .= '1';
73 - }
74 - $page->slug = $slug;
75 $page->priority = $this->bookRepo->getNewPriority($book); 77 $page->priority = $this->bookRepo->getNewPriority($book);
76 78
77 - if($request->has('parent')) { 79 + if($request->has('chapter') && $this->chapterRepo->idExists($request->get('chapter'))) {
78 - $page->page_id = $request->get('parent'); 80 + $page->chapter_id = $request->get('chapter');
79 } 81 }
80 82
81 $page->book_id = $book->id; 83 $page->book_id = $book->id;
...@@ -95,9 +97,8 @@ class PageController extends Controller ...@@ -95,9 +97,8 @@ class PageController extends Controller
95 { 97 {
96 $book = $this->bookRepo->getBySlug($bookSlug); 98 $book = $this->bookRepo->getBySlug($bookSlug);
97 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 99 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
98 - $breadCrumbs = $this->pageRepo->getBreadCrumbs($page);
99 //dd($sidebarBookTree); 100 //dd($sidebarBookTree);
100 - return view('pages/show', ['page' => $page, 'breadCrumbs' => $breadCrumbs, 'book' => $book]); 101 + return view('pages/show', ['page' => $page, 'book' => $book]);
101 } 102 }
102 103
103 /** 104 /**
...@@ -111,7 +112,7 @@ class PageController extends Controller ...@@ -111,7 +112,7 @@ class PageController extends Controller
111 { 112 {
112 $book = $this->bookRepo->getBySlug($bookSlug); 113 $book = $this->bookRepo->getBySlug($bookSlug);
113 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 114 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
114 - return view('pages/edit', ['page' => $page]); 115 + return view('pages/edit', ['page' => $page, 'book' => $book]);
115 } 116 }
116 117
117 /** 118 /**
...@@ -127,10 +128,7 @@ class PageController extends Controller ...@@ -127,10 +128,7 @@ class PageController extends Controller
127 $book = $this->bookRepo->getBySlug($bookSlug); 128 $book = $this->bookRepo->getBySlug($bookSlug);
128 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 129 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
129 $page->fill($request->all()); 130 $page->fill($request->all());
130 - $slug = Str::slug($page->name); 131 + $page->slug = $this->pageRepo->findSuitableSlug($page->name, $book->id, $page->id);
131 - while($this->pageRepo->countBySlug($slug, $book->id) > 0 && $slug != $pageSlug) {
132 - $slug .= '1';
133 - }
134 $page->text = strip_tags($page->html); 132 $page->text = strip_tags($page->html);
135 $page->save(); 133 $page->save();
136 return redirect($page->getUrl()); 134 return redirect($page->getUrl());
...@@ -170,19 +168,36 @@ class PageController extends Controller ...@@ -170,19 +168,36 @@ class PageController extends Controller
170 public function sortPages($bookSlug) 168 public function sortPages($bookSlug)
171 { 169 {
172 $book = $this->bookRepo->getBySlug($bookSlug); 170 $book = $this->bookRepo->getBySlug($bookSlug);
173 - $tree = $this->bookRepo->getTree($book); 171 + return view('pages/sort', ['book' => $book]);
174 - return view('pages/sort', ['book' => $book, 'tree' => $tree]);
175 } 172 }
176 173
174 + /**
175 + * Saves an array of sort mapping to pages and chapters.
176 + *
177 + * @param $bookSlug
178 + * @param Request $request
179 + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
180 + */
177 public function savePageSort($bookSlug, Request $request) 181 public function savePageSort($bookSlug, Request $request)
178 { 182 {
179 $book = $this->bookRepo->getBySlug($bookSlug); 183 $book = $this->bookRepo->getBySlug($bookSlug);
184 + // Return if no map sent
180 if(!$request->has('sort-tree')) { 185 if(!$request->has('sort-tree')) {
181 return redirect($book->getUrl()); 186 return redirect($book->getUrl());
182 } 187 }
183 188
189 + // Sort pages and chapters
184 $sortMap = json_decode($request->get('sort-tree')); 190 $sortMap = json_decode($request->get('sort-tree'));
185 - $this->pageRepo->applySortMap($sortMap, $book->id); 191 + foreach($sortMap as $index => $bookChild) {
192 + $id = $bookChild->id;
193 + $isPage = $bookChild->type == 'page';
194 + $model = $isPage ? $this->pageRepo->getById($id) : $this->chapterRepo->getById($id);
195 + $model->priority = $index;
196 + if($isPage) {
197 + $model->chapter_id = ($bookChild->parentChapter === false) ? 0 : $bookChild->parentChapter;
198 + }
199 + $model->save();
200 + }
186 return redirect($book->getUrl()); 201 return redirect($book->getUrl());
187 } 202 }
188 203
......
...@@ -28,12 +28,12 @@ Route::group(['prefix' => 'books'], function() { ...@@ -28,12 +28,12 @@ Route::group(['prefix' => 'books'], function() {
28 Route::get('/{bookSlug}/sort', 'PageController@sortPages'); 28 Route::get('/{bookSlug}/sort', 'PageController@sortPages');
29 Route::put('/{bookSlug}/sort', 'PageController@savePageSort'); 29 Route::put('/{bookSlug}/sort', 'PageController@savePageSort');
30 Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show'); 30 Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
31 - Route::get('/{bookSlug}/page/{pageSlug}/create', 'PageController@create');
32 Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit'); 31 Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
33 Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); 32 Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
34 Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update'); 33 Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
35 Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy'); 34 Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy');
36 35
36 + Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
37 Route::get('/{bookSlug}/chapter/create', 'ChapterController@create'); 37 Route::get('/{bookSlug}/chapter/create', 'ChapterController@create');
38 Route::post('/{bookSlug}/chapter/create', 'ChapterController@store'); 38 Route::post('/{bookSlug}/chapter/create', 'ChapterController@store');
39 Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show'); 39 Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show');
......
...@@ -22,6 +22,16 @@ class Page extends Model ...@@ -22,6 +22,16 @@ class Page extends Model
22 return $this->belongsTo('Oxbow\Book'); 22 return $this->belongsTo('Oxbow\Book');
23 } 23 }
24 24
25 + public function chapter()
26 + {
27 + return $this->belongsTo('Oxbow\Chapter');
28 + }
29 +
30 + public function hasChapter()
31 + {
32 + return $this->chapter()->count() > 0;
33 + }
34 +
25 public function parent() 35 public function parent()
26 { 36 {
27 return $this->belongsTo('Oxbow\Page', 'page_id'); 37 return $this->belongsTo('Oxbow\Page', 'page_id');
...@@ -32,4 +42,9 @@ class Page extends Model ...@@ -32,4 +42,9 @@ class Page extends Model
32 return '/books/' . $this->book->slug . '/page/' . $this->slug; 42 return '/books/' . $this->book->slug . '/page/' . $this->slug;
33 } 43 }
34 44
45 + public function getExcerpt($length = 100)
46 + {
47 + return strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
48 + }
49 +
35 } 50 }
......
...@@ -18,6 +18,11 @@ class ChapterRepo ...@@ -18,6 +18,11 @@ class ChapterRepo
18 $this->chapter = $chapter; 18 $this->chapter = $chapter;
19 } 19 }
20 20
21 + public function idExists($id)
22 + {
23 + return $this->chapter->where('id', '=', $id)->count() > 0;
24 + }
25 +
21 public function getById($id) 26 public function getById($id)
22 { 27 {
23 return $this->chapter->findOrFail($id); 28 return $this->chapter->findOrFail($id);
......
...@@ -17,6 +17,11 @@ class PageRepo ...@@ -17,6 +17,11 @@ class PageRepo
17 $this->page = $page; 17 $this->page = $page;
18 } 18 }
19 19
20 + public function idExists($id)
21 + {
22 + return $this->page->where('page_id', '=', $id)->count() > 0;
23 + }
24 +
20 public function getById($id) 25 public function getById($id)
21 { 26 {
22 return $this->page->findOrFail($id); 27 return $this->page->findOrFail($id);
...@@ -59,41 +64,38 @@ class PageRepo ...@@ -59,41 +64,38 @@ class PageRepo
59 return $query->get(); 64 return $query->get();
60 } 65 }
61 66
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 /** 67 /**
74 - * Gets the pages at the top of the page hierarchy. 68 + * Checks if a slug exists within a book already.
69 + * @param $slug
75 * @param $bookId 70 * @param $bookId
71 + * @param bool|false $currentId
72 + * @return bool
76 */ 73 */
77 - private function getTopLevelPages($bookId) 74 + public function doesSlugExist($slug, $bookId, $currentId = false)
78 { 75 {
79 - return $this->page->where('book_id', '=', $bookId)->where('chapter_id', '=', 0)->orderBy('priority')->get(); 76 + $query = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId);
77 + if($currentId) {
78 + $query = $query->where('id', '!=', $currentId);
79 + }
80 + return $query->count() > 0;
80 } 81 }
81 82
82 /** 83 /**
83 - * Applies a sort map to all applicable pages. 84 + * Gets a suitable slug for the resource
84 - * @param $sortMap 85 + *
86 + * @param $name
85 * @param $bookId 87 * @param $bookId
88 + * @param bool|false $currentId
89 + * @return string
86 */ 90 */
87 - public function applySortMap($sortMap, $bookId) 91 + public function findSuitableSlug($name, $bookId, $currentId = false)
88 { 92 {
89 - foreach($sortMap as $index => $map) { 93 + $slug = Str::slug($name);
90 - $page = $this->getById($map->id); 94 + while($this->doesSlugExist($slug, $bookId, $currentId)) {
91 - if($page->book_id === $bookId) { 95 + $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
92 - $page->page_id = $map->parent;
93 - $page->priority = $index;
94 - $page->save();
95 - }
96 } 96 }
97 + return $slug;
97 } 98 }
98 99
100 +
99 } 101 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 "dropzone": "~4.0.1", 17 "dropzone": "~4.0.1",
18 "tinymce-dist": "~4.2.1", 18 "tinymce-dist": "~4.2.1",
19 "bootstrap": "~3.3.5", 19 "bootstrap": "~3.3.5",
20 - "jquery-sortable": "~0.9.13" 20 + "jquery-sortable": "~0.9.13",
21 + "material-design-iconic-font": "~2.1.1"
21 } 22 }
22 } 23 }
......
...@@ -59,3 +59,30 @@ $button-border-radius: 3px; ...@@ -59,3 +59,30 @@ $button-border-radius: 3px;
59 } 59 }
60 } 60 }
61 } 61 }
62 +
63 +// Floating action button
64 +//.fab {
65 +// $size: 70px;
66 +// button.button {
67 +// border-radius: 100%;
68 +// width: $size;
69 +// height: $size;
70 +// font-size: 48px;
71 +// text-align: center;
72 +// margin: 0;
73 +// padding: 0;
74 +// border: 0;
75 +// box-shadow: 0 0 2px 2px #DDD;
76 +// transition: all ease-in-out 160ms;
77 +// i {
78 +// transform: rotate(0deg);
79 +// transition: all ease-in-out 160ms;
80 +// }
81 +// &:hover {
82 +// box-shadow: 0 2px 4px 2px #CCC;
83 +// i {
84 +// transform: rotate(180deg);
85 +// }
86 +// }
87 +// }
88 +//}
......
...@@ -7,8 +7,8 @@ h1 { ...@@ -7,8 +7,8 @@ h1 {
7 line-height: 1.22222222em; 7 line-height: 1.22222222em;
8 margin-top: 0.48888889em; 8 margin-top: 0.48888889em;
9 margin-bottom: 0.48888889em; 9 margin-bottom: 0.48888889em;
10 - padding-bottom: 0.3333em; 10 + //padding-bottom: 0.3333em;
11 - border-bottom: 1px solid #EAEAEA; 11 + //border-bottom: 1px solid #EAEAEA;
12 //margin-left: -$-xxl; 12 //margin-left: -$-xxl;
13 //margin-right: -$-xxl; 13 //margin-right: -$-xxl;
14 } 14 }
...@@ -73,7 +73,7 @@ hr { ...@@ -73,7 +73,7 @@ hr {
73 border: 0; 73 border: 0;
74 height: 1px; 74 height: 1px;
75 border: 0; 75 border: 0;
76 - background: #e3e0e0; 76 + background: #EAEAEA;
77 margin-bottom: $-l; 77 margin-bottom: $-l;
78 &.faded { 78 &.faded {
79 background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF); 79 background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
...@@ -151,10 +151,16 @@ span.code { ...@@ -151,10 +151,16 @@ span.code {
151 */ 151 */
152 p.pos, p .pos, span.pos, .text-pos { 152 p.pos, p .pos, span.pos, .text-pos {
153 color: $positive; 153 color: $positive;
154 + &:hover {
155 + color: $positive;
156 + }
154 } 157 }
155 158
156 p.neg, p .neg, span.neg, .text-neg { 159 p.neg, p .neg, span.neg, .text-neg {
157 color: $negative; 160 color: $negative;
161 + &:hover {
162 + color: $negative;
163 + }
158 } 164 }
159 165
160 p.muted, p .muted, span.muted, .text-muted { 166 p.muted, p .muted, span.muted, .text-muted {
...@@ -163,10 +169,16 @@ p.muted, p .muted, span.muted, .text-muted { ...@@ -163,10 +169,16 @@ p.muted, p .muted, span.muted, .text-muted {
163 169
164 p.primary, p .primary, span.primary, .text-primary { 170 p.primary, p .primary, span.primary, .text-primary {
165 color: $primary; 171 color: $primary;
172 + &:hover {
173 + color: $primary;
174 + }
166 } 175 }
167 176
168 p.secondary, p .secondary, span.secondary, .text-secondary { 177 p.secondary, p .secondary, span.secondary, .text-secondary {
169 color: $secondary; 178 color: $secondary;
179 + &:hover {
180 + color: $secondary;
181 + }
170 } 182 }
171 183
172 /* 184 /*
......
...@@ -67,7 +67,7 @@ ul.menu { ...@@ -67,7 +67,7 @@ ul.menu {
67 li a { 67 li a {
68 padding: $-m; 68 padding: $-m;
69 display: block; 69 display: block;
70 - border-bottom: 1px solid #333; 70 + border-bottom: 1px solid #3A3939;
71 } 71 }
72 } 72 }
73 73
...@@ -102,13 +102,21 @@ ul.menu { ...@@ -102,13 +102,21 @@ ul.menu {
102 } 102 }
103 103
104 .page-list { 104 .page-list {
105 - a { 105 + h3 {
106 + margin: $-l 0;
107 + }
108 + .inset-list {
109 + display: block;
110 + overflow: hidden;
111 + padding-left: $-l*2;
112 + border-top: 3px dotted #EEE;
113 + }
114 + h4 {
106 display: block; 115 display: block;
107 - padding: $-s 0; 116 + margin: $-m 0;
108 - border-bottom: 2px dotted #CCC;
109 - &:first-child {
110 - border-top: 2px dotted #CCC;
111 } 117 }
118 + hr {
119 + margin-top: 0;
112 } 120 }
113 } 121 }
114 122
...@@ -247,16 +255,20 @@ h1, h2, h3, h4, h5, h6 { ...@@ -247,16 +255,20 @@ h1, h2, h3, h4, h5, h6 {
247 padding-right: 4px; 255 padding-right: 4px;
248 } 256 }
249 span.sep { 257 span.sep {
250 - color: #888; 258 + color: #aaa;
251 padding: 0 $-xs; 259 padding: 0 $-xs;
252 } 260 }
253 } 261 }
254 262
255 .faded { 263 .faded {
264 + a {
265 + color: #666;
256 opacity: 0.5; 266 opacity: 0.5;
257 - transition: opacity ease-in-out 120ms; 267 + transition: all ease-in-out 120ms;
258 &:hover { 268 &:hover {
259 opacity: 1; 269 opacity: 1;
270 + text-decoration: none;
271 + }
260 } 272 }
261 } 273 }
262 274
...@@ -277,68 +289,39 @@ h1, h2, h3, h4, h5, h6 { ...@@ -277,68 +289,39 @@ h1, h2, h3, h4, h5, h6 {
277 } 289 }
278 290
279 291
280 -.nested-page-list {
281 - list-style: none;
282 - margin-left: 0;
283 - li {
284 - border-top: 3px dotted #BBB;
285 - padding: $-s 0;
286 - user-select: none;
287 - }
288 - li:last-child {
289 - border-bottom: 3px dotted #BBB;
290 - }
291 - .nested-page-list {
292 - margin-top: $-xs;
293 - display: none;
294 - margin: $-xs 0 $-xs 9px;
295 - font-size: $fs-m * 0.9;
296 - border-left: 2px solid #EEE;
297 - }
298 - .nested-page-list li {
299 - border: none;
300 - padding-right: $-m;
301 - padding-left: $-m;
302 - &.expanded.has-children {
303 - padding-bottom: 0;
304 - }
305 - }
306 - i.arrow {
307 - font-size: 0.8em;
308 - padding: $-xs;
309 - margin-top: -$-xs;
310 - margin-bottom: -$-xs;
311 - transform-origin: 50% 50%;
312 - transition: transform ease-in-out 180ms;
313 - cursor: pointer;
314 - }
315 - li.expanded {
316 - > i.arrow {
317 - transform: rotate(90deg);
318 - }
319 - >.nested-page-list {
320 - display: block;
321 - }
322 - }
323 -}
324 -
325 .book-tree h4 { 292 .book-tree h4 {
326 padding: $-m $-s 0 $-s; 293 padding: $-m $-s 0 $-s;
327 i { 294 i {
328 padding-right: $-s; 295 padding-right: $-s;
329 } 296 }
330 } 297 }
298 +// Sidebar list
331 .book-tree .sidebar-page-list { 299 .book-tree .sidebar-page-list {
332 list-style: none; 300 list-style: none;
333 margin: 0; 301 margin: 0;
334 li a { 302 li a {
335 display: block; 303 display: block;
336 - padding: $-s $-m; 304 + border-bottom: 1px solid #3A3939;
337 - border-bottom: 2px dotted #333;
338 } 305 }
339 a.bold { 306 a.bold {
340 color: #EEE !important; 307 color: #EEE !important;
341 } 308 }
309 + ul {
310 + list-style: none;
311 + margin: 0;
312 + }
313 + ul li a {
314 + padding-left: $-xl;
315 + }
316 + .book {
317 + color: #7BD06E !important;
318 + }
319 + .chapter {
320 + color: #D2A64B !important;
321 + }
322 + .page {
323 + color: #4599DC !important;
324 + }
342 } 325 }
343 326
344 .sortable-page-list, .sortable-page-list ul { 327 .sortable-page-list, .sortable-page-list ul {
...@@ -380,8 +363,3 @@ body.dragging, body.dragging * { ...@@ -380,8 +363,3 @@ body.dragging, body.dragging * {
380 .sortable-page-list li.placeholder:before { 363 .sortable-page-list li.placeholder:before {
381 position: absolute; 364 position: absolute;
382 } 365 }
...\ No newline at end of file ...\ No newline at end of file
383 -
384 -.material-icons {
385 - font-size: 1em;
386 - line-height: 1.4em;
387 -}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
5 <meta name="viewport" content="width=device-width"> 5 <meta name="viewport" content="width=device-width">
6 <link rel="stylesheet" href="/css/app.css"> 6 <link rel="stylesheet" href="/css/app.css">
7 <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> 7 <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
8 - <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"> 8 + {{--<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">--}}
9 + <link rel="stylesheet" href="/bower/material-design-iconic-font/dist/css/material-design-iconic-font.min.css">
9 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> 10 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
10 <script src="/bower/bootstrap/dist/js/bootstrap.js"></script> 11 <script src="/bower/bootstrap/dist/js/bootstrap.js"></script>
11 <script src="/bower/jquery-sortable/source/js/jquery-sortable.js"></script> 12 <script src="/bower/jquery-sortable/source/js/jquery-sortable.js"></script>
...@@ -42,8 +43,13 @@ ...@@ -42,8 +43,13 @@
42 </form> 43 </form>
43 </div> 44 </div>
44 <ul class="menu"> 45 <ul class="menu">
45 - <li><a href="/books"><i class="fa fa-book"></i>Books</a></li> 46 + <li><a href="/books"><i class="zmdi zmdi-book"></i>Books</a></li>
46 </ul> 47 </ul>
48 + @if(isset($book) && !isset($books))
49 + <div class="book-tree">
50 + @include('pages/sidebar-tree-list', ['book' => $book])
51 + </div>
52 + @endif
47 @yield('sidebar') 53 @yield('sidebar')
48 </section> 54 </section>
49 55
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
7 <div class="col-md-6"></div> 7 <div class="col-md-6"></div>
8 <div class="col-md-6 faded"> 8 <div class="col-md-6 faded">
9 <div class="action-buttons"> 9 <div class="action-buttons">
10 - <a href="/books/create">+ Add new book</a> 10 + <a href="/books/create" class="text-pos"><i class="zmdi zmdi-plus"></i>Add new book</a>
11 </div> 11 </div>
12 </div> 12 </div>
13 </div> 13 </div>
......
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
6 <div class="col-md-6"></div> 6 <div class="col-md-6"></div>
7 <div class="col-md-6"> 7 <div class="col-md-6">
8 <div class="action-buttons faded"> 8 <div class="action-buttons faded">
9 - <a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit</a> 9 + <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Page</a>
10 - <a href="{{ $book->getUrl() }}/sort"><i class="fa fa-sort"></i>Sort</a> 10 + <a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Chapter</a>
11 - <a href="{{ $book->getUrl() }}/delete"><i class="fa fa-trash"></i>Delete</a> 11 + <a href="{{$book->getEditUrl()}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a>
12 + <a href="{{ $book->getUrl() }}/sort" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a>
13 + <a href="{{ $book->getUrl() }}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
12 </div> 14 </div>
13 </div> 15 </div>
14 </div> 16 </div>
...@@ -17,44 +19,45 @@ ...@@ -17,44 +19,45 @@
17 <h1>{{$book->name}}</h1> 19 <h1>{{$book->name}}</h1>
18 <p class="text-muted">{{$book->description}}</p> 20 <p class="text-muted">{{$book->description}}</p>
19 21
20 - <div class="clearfix header-group"> 22 + <div class="page-list">
21 - <h4 class="float">Contents</h4> 23 + <hr>
22 - <div class="float right">
23 - <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos">+ New Page</a>
24 - <a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos">+ New Chapter</a>
25 - </div>
26 - </div>
27 -
28 - <div>
29 @foreach($book->children() as $childElement) 24 @foreach($book->children() as $childElement)
30 - <div > 25 + <div class="book-child">
31 <h3> 26 <h3>
32 <a href="{{ $childElement->getUrl() }}"> 27 <a href="{{ $childElement->getUrl() }}">
33 @if(is_a($childElement, 'Oxbow\Chapter')) 28 @if(is_a($childElement, 'Oxbow\Chapter'))
34 - <i class="fa fa-archive"></i> 29 + <i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
35 @else 30 @else
36 - <i class="fa fa-file"></i> 31 + <i class="zmdi zmdi-file-text"></i>
37 @endif 32 @endif
38 {{ $childElement->name }} 33 {{ $childElement->name }}
39 </a> 34 </a>
40 </h3> 35 </h3>
36 + <p class="text-muted">
37 + {{$childElement->getExcerpt()}}
38 + </p>
39 +
40 + @if(is_a($childElement, 'Oxbow\Chapter'))
41 + <div class="inset-list">
42 + @foreach($childElement->pages as $page)
43 + <h4><a href="{{$page->getUrl()}}"><i class="zmdi zmdi-file-text"></i> {{$page->name}}</a></h4>
44 + @endforeach
45 + </div>
46 + @endif
41 </div> 47 </div>
42 <hr> 48 <hr>
43 @endforeach 49 @endforeach
44 </div> 50 </div>
45 51
46 - {{--@include('pages/page-tree-list', ['pageTree' => $pageTree])--}}
47 -
48 </div> 52 </div>
49 53
50 54
51 <script> 55 <script>
52 $(function() { 56 $(function() {
53 57
54 - $('.nested-page-list i.arrow').click(function() { 58 + $('.chapter-toggle').click(function(e) {
55 - var list = $(this).closest('.nested-page-list'); 59 + e.preventDefault();
56 - var listItem = $(this).closest('li'); 60 + $(this).closest('.book-child').find('.inset-list').slideToggle(180);
57 - listItem.toggleClass('expanded');
58 }); 61 });
59 62
60 }); 63 });
......
...@@ -3,11 +3,16 @@ ...@@ -3,11 +3,16 @@
3 @section('content') 3 @section('content')
4 4
5 <div class="row faded-small"> 5 <div class="row faded-small">
6 - <div class="col-md-6"></div> 6 + <div class="col-md-6 faded">
7 + <div class="breadcrumbs padded-horizontal">
8 + <a href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a>
9 + </div>
10 + </div>
7 <div class="col-md-6 faded"> 11 <div class="col-md-6 faded">
8 <div class="action-buttons"> 12 <div class="action-buttons">
9 - <a href="{{$chapter->getUrl() . '/edit'}}" ><i class="fa fa-pencil"></i>Edit</a> 13 + <a href="{{$chapter->getUrl() . '/create-page'}}" class="text-pos"><i class="zmdi zmdi-plus"></i>New Page</a>
10 - <a href="{{$chapter->getUrl() . '/delete'}}"><i class="fa fa-trash"></i>Delete</a> 14 + <a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a>
15 + <a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
11 </div> 16 </div>
12 </div> 17 </div>
13 </div> 18 </div>
...@@ -16,21 +21,26 @@ ...@@ -16,21 +21,26 @@
16 <div class="page-content"> 21 <div class="page-content">
17 <h1>{{ $chapter->name }}</h1> 22 <h1>{{ $chapter->name }}</h1>
18 <p class="text-muted">{{ $chapter->description }}</p> 23 <p class="text-muted">{{ $chapter->description }}</p>
24 +
19 @if(count($chapter->pages) > 0) 25 @if(count($chapter->pages) > 0)
20 - <h4 class="text-muted">Pages</h4>
21 <div class="page-list"> 26 <div class="page-list">
27 + <hr>
22 @foreach($chapter->pages as $page) 28 @foreach($chapter->pages as $page)
23 <div > 29 <div >
24 <h3> 30 <h3>
25 <a href="{{ $page->getUrl() }}"> 31 <a href="{{ $page->getUrl() }}">
26 - <i class="fa fa-file"></i> 32 + <i class="zmdi zmdi-file-text"></i>{{ $page->name }}
27 - {{ $page->name }}
28 </a> 33 </a>
29 </h3> 34 </h3>
35 + <p class="text-muted">
36 + {{$page->getExcerpt()}}
37 + </p>
30 </div> 38 </div>
31 <hr> 39 <hr>
32 @endforeach 40 @endforeach
33 </div> 41 </div>
42 + @else
43 + <p class="text-muted">No pages are in this chapter</p>
34 @endif 44 @endif
35 </div> 45 </div>
36 46
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
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) 12 + @if($chapter)
13 - <input type="hidden" name="parent" value="{{$parentPage->id}}"> 13 + <input type="hidden" name="chapter" value="{{$chapter->id}}">
14 @endif 14 @endif
15 </form> 15 </form>
16 @stop 16 @stop
......
1 -{{--Requires an array of pages to be passed as $pageTree--}}
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
1 -
2 -<li data-id="{{$page['id']}}">{{ $page['name'] }}
3 - <ul>
4 - @if($page['hasChildren'])
5 - @foreach($page['pages'] as $childPage)
6 - @include('pages/page-tree-sort', ['page'=>$childPage])
7 - @endforeach
8 - @endif
9 - </ul>
10 -</li>
1 @extends('base') 1 @extends('base')
2 2
3 -@section('sidebar')
4 - <div class="book-tree">
5 - <h4><a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{$book->name}}</a></h4>
6 - @include('pages/sidebar-tree-list', ['book' => $book])
7 - </div>
8 -@stop
9 -
10 @section('content') 3 @section('content')
11 4
12 <div class="row faded-small"> 5 <div class="row faded-small">
13 <div class="col-md-6 faded"> 6 <div class="col-md-6 faded">
14 <div class="breadcrumbs padded-horizontal"> 7 <div class="breadcrumbs padded-horizontal">
15 - <a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{ $book->name }}</a> 8 + <a href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a>
16 - @if($breadCrumbs) 9 + @if($page->hasChapter())
17 - @foreach($breadCrumbs as $parentPage) 10 + <span class="sep">&raquo;</span>
18 - <span class="sep">&gt;</span> 11 + <a href="{{ $page->chapter->getUrl() }}">
19 - <a href="{{$parentPage->getUrl()}}">{{ $parentPage->name }}</a> 12 + <i class="zmdi zmdi-collection-bookmark"></i>
20 - @endforeach 13 + {{$page->chapter->name}}
14 + </a>
21 @endif 15 @endif
22 </div> 16 </div>
23 </div> 17 </div>
24 <div class="col-md-6 faded"> 18 <div class="col-md-6 faded">
25 <div class="action-buttons"> 19 <div class="action-buttons">
26 - <a href="{{$page->getUrl() . '/edit'}}" ><i class="fa fa-pencil"></i>Edit</a> 20 + <a href="{{$page->getUrl() . '/edit'}}" class="text-primary" ><i class="zmdi zmdi-edit"></i>Edit</a>
27 - <a href="{{$page->getUrl() . '/delete'}}"><i class="fa fa-trash"></i>Delete</a> 21 + <a href="{{$page->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
28 </div> 22 </div>
29 </div> 23 </div>
30 </div> 24 </div>
...@@ -57,6 +51,7 @@ ...@@ -57,6 +51,7 @@
57 var pageNav = $('.page-nav-list'); 51 var pageNav = $('.page-nav-list');
58 var pageContent = $('.page-content'); 52 var pageContent = $('.page-content');
59 var headers = pageContent.find('h1, h2, h3, h4, h5, h6'); 53 var headers = pageContent.find('h1, h2, h3, h4, h5, h6');
54 + if(headers.length > 5) {
60 headers.each(function() { 55 headers.each(function() {
61 var header = $(this); 56 var header = $(this);
62 var tag = header.prop('tagName'); 57 var tag = header.prop('tagName');
...@@ -69,6 +64,10 @@ ...@@ -69,6 +64,10 @@
69 header.smoothScrollTo(); 64 header.smoothScrollTo();
70 }) 65 })
71 }); 66 });
67 + } else {
68 + $('.side-nav').hide();
69 + }
70 +
72 71
73 // Set up link hooks 72 // Set up link hooks
74 var pageId = {{$page->id}}; 73 var pageId = {{$page->id}};
......
1 -{{--Requires an array of pages to be passed as $pageTree--}}
2 1
3 -<ul class="sidebar-page-list"> 2 +<ul class="sidebar-page-list menu">
3 + <li class="book-header"><a href="{{$book->getUrl()}}" class="book"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
4 @foreach($book->children() as $bookChild) 4 @foreach($book->children() as $bookChild)
5 <li> 5 <li>
6 + <a href="{{$bookChild->getUrl()}}" class="@if(is_a($bookChild, 'Oxbow\Chapter')) chapter @else page @endif">
7 + @if(is_a($bookChild, 'Oxbow\Chapter'))
8 + <i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
9 + @else
10 + <i class="zmdi zmdi-file-text"></i>
11 + @endif
6 {{ $bookChild->name }} 12 {{ $bookChild->name }}
13 + </a>
14 +
7 @if(is_a($bookChild, 'Oxbow\Chapter') && count($bookChild->pages) > 0) 15 @if(is_a($bookChild, 'Oxbow\Chapter') && count($bookChild->pages) > 0)
8 - <ul> 16 + <ul class="menu">
9 - @foreach($pages as $page) 17 + @foreach($bookChild->pages as $page)
10 - <li>{{ $page->name }}</li> 18 + <li>
19 + <a href="{{$page->getUrl()}}" class="@if(is_a($page, 'Oxbow\Chapter')) chapter @else page @endif">
20 + @if(is_a($page, 'Oxbow\Chapter'))
21 + <i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
22 + @else
23 + <i class="zmdi zmdi-file-text"></i>
24 + @endif
25 + {{ $page->name }}
26 + </a>
27 + </li>
11 @endforeach 28 @endforeach
12 </ul> 29 </ul>
13 @endif 30 @endif
......
...@@ -2,31 +2,35 @@ ...@@ -2,31 +2,35 @@
2 2
3 @section('content') 3 @section('content')
4 4
5 - <div class="row"> 5 + <div class="page-content">
6 - <div class="page-menu col-md-3"> 6 + <h1>{{ $book->name }} <span class="subheader">Sort Pages</span></h1>
7 - <div class="page-actions"> 7 +
8 + <ul class="sortable-page-list" id="sort-list">
9 + @foreach($book->children() as $bookChild)
10 + <li data-id="{{$bookChild->id}}" data-type="{{ is_a($bookChild, 'Oxbow\Chapter') ? 'chapter' : 'page' }}">
11 + {{ $bookChild->name }}
12 + @if(is_a($bookChild, 'Oxbow\Chapter'))
13 + <ul>
14 + @foreach($bookChild->pages as $page)
15 + <li data-id="{{$page->id}}" data-type="page">
16 + {{ $page->name }}
17 + </li>
18 + @endforeach
19 + </ul>
20 + @endif
21 + </li>
22 + @endforeach
23 + </ul>
24 +
8 <form action="{{$book->getUrl()}}/sort" method="POST"> 25 <form action="{{$book->getUrl()}}/sort" method="POST">
9 {!! csrf_field() !!} 26 {!! csrf_field() !!}
10 <input type="hidden" name="_method" value="PUT"> 27 <input type="hidden" name="_method" value="PUT">
11 <input type="hidden" id="sort-tree-input" name="sort-tree"> 28 <input type="hidden" id="sort-tree-input" name="sort-tree">
12 - <h4>Actions</h4>
13 <div class="list"> 29 <div class="list">
14 <button class="button pos" type="submit">Save Ordering</button> 30 <button class="button pos" type="submit">Save Ordering</button>
15 </div> 31 </div>
16 </form> 32 </form>
17 - </div>
18 - </div>
19 -
20 - <div class="page-content right col-md-9">
21 - <h1>{{ $book->name }} <span class="subheader">Sort Pages</span></h1>
22 33
23 - <ul class="sortable-page-list" id="sort-list">
24 - @foreach($tree['pages'] as $treePage)
25 - @include('pages/page-tree-sort', ['page' => $treePage])
26 - @endforeach
27 - </ul>
28 -
29 - </div>
30 </div> 34 </div>
31 35
32 <script> 36 <script>
...@@ -36,25 +40,36 @@ ...@@ -36,25 +40,36 @@
36 group: 'serialization', 40 group: 'serialization',
37 onDrop: function($item, container, _super) { 41 onDrop: function($item, container, _super) {
38 var data = group.sortable('serialize').get(); 42 var data = group.sortable('serialize').get();
39 - console.log(data); 43 + var pageMap = buildPageMap(data[0]);
40 - var pageMap = [];
41 - var parent = 0;
42 - buildPageMap(pageMap, parent, data[0]);
43 $('#sort-tree-input').val(JSON.stringify(pageMap)); 44 $('#sort-tree-input').val(JSON.stringify(pageMap));
44 _super($item, container); 45 _super($item, container);
45 } 46 }
46 }); 47 });
47 48
48 - function buildPageMap(pageMap, parent, data) { 49 + function buildPageMap(data) {
50 + var pageMap = [];
49 for(var i = 0; i < data.length; i++) { 51 for(var i = 0; i < data.length; i++) {
50 - var page = data[i]; 52 + var bookChild = data[i];
53 + pageMap.push({
54 + id: bookChild.id,
55 + parentChapter: false,
56 + type: bookChild.type
57 + });
58 + if(bookChild.type == 'chapter' && bookChild.children) {
59 + var chapterId = bookChild.id;
60 + var chapterChildren = bookChild.children[0];
61 + for(var j = 0; j < chapterChildren.length; j++) {
62 + var page = chapterChildren[j];
51 pageMap.push({ 63 pageMap.push({
52 id: page.id, 64 id: page.id,
53 - parent: parent 65 + parentChapter: chapterId,
66 + type: 'page'
54 }); 67 });
55 - buildPageMap(pageMap, page.id, page.children[0]);
56 } 68 }
57 } 69 }
70 + }
71 + return pageMap;
72 + }
58 73
59 }); 74 });
60 </script> 75 </script>
......