From b54ef3bb4b7c51f43382660d497c265b10de1361 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Thu, 30 Jul 2015 22:27:35 +0100 Subject: [PATCH] Made chapters functional and cleaned design features --- app/Book.php | 2 +- app/Chapter.php | 7 +- app/Http/Controllers/PageController.php | 63 +++++++---- app/Http/routes.php | 2 +- app/Page.php | 15 +++ app/Repos/ChapterRepo.php | 5 + app/Repos/PageRepo.php | 54 ++++----- bower.json | 3 +- resources/assets/sass/_buttons.scss | 27 +++++ resources/assets/sass/_text.scss | 22 +++- resources/assets/sass/styles.scss | 106 +++++++----------- resources/views/base.blade.php | 10 +- resources/views/books/index.blade.php | 2 +- resources/views/books/show.blade.php | 45 ++++---- resources/views/chapters/show.blade.php | 22 +++- resources/views/pages/create.blade.php | 4 +- .../views/pages/page-tree-list.blade.php | 15 --- .../views/pages/page-tree-sort.blade.php | 10 -- resources/views/pages/show.blade.php | 53 +++++---- .../views/pages/sidebar-tree-list.blade.php | 29 ++++- resources/views/pages/sort.blade.php | 79 +++++++------ 21 files changed, 330 insertions(+), 245 deletions(-) delete mode 100644 resources/views/pages/page-tree-list.blade.php delete mode 100644 resources/views/pages/page-tree-sort.blade.php diff --git a/app/Book.php b/app/Book.php index cdad27879..ccf02e4f3 100644 --- a/app/Book.php +++ b/app/Book.php @@ -31,7 +31,7 @@ class Book extends Model public function children() { - $pages = $this->pages()->get(); + $pages = $this->pages()->where('chapter_id', '=', 0)->get(); $chapters = $this->chapters()->get(); $children = $pages->merge($chapters); return $children->sortBy('priority'); diff --git a/app/Chapter.php b/app/Chapter.php index f4e9dce66..77c161385 100644 --- a/app/Chapter.php +++ b/app/Chapter.php @@ -12,7 +12,7 @@ class Chapter extends Model return $this->belongsTo('Oxbow\Book'); } - public function children() + public function pages() { return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC'); } @@ -22,4 +22,9 @@ class Chapter extends Model return '/books/' . $this->book->slug . '/chapter/' . $this->slug; } + public function getExcerpt($length = 100) + { + return strlen($this->description) > $length ? substr($this->description, 0, $length-3) . '...' : $this->description; + } + } diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 25009f768..c84bc803f 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -7,6 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Str; use Oxbow\Http\Requests; use Oxbow\Repos\BookRepo; +use Oxbow\Repos\ChapterRepo; use Oxbow\Repos\PageRepo; class PageController extends Controller @@ -14,16 +15,19 @@ class PageController extends Controller protected $pageRepo; protected $bookRepo; + protected $chapterRepo; /** * PageController constructor. - * @param $pageRepo - * @param $bookRepo + * @param PageRepo $pageRepo + * @param BookRepo $bookRepo + * @param ChapterRepo $chapterRepo */ - public function __construct(PageRepo $pageRepo, BookRepo $bookRepo) + public function __construct(PageRepo $pageRepo, BookRepo $bookRepo, ChapterRepo $chapterRepo) { $this->pageRepo = $pageRepo; $this->bookRepo = $bookRepo; + $this->chapterRepo = $chapterRepo; } @@ -41,14 +45,15 @@ class PageController extends Controller * Show the form for creating a new resource. * * @param $bookSlug - * @param bool $pageSlug + * @param bool $chapterSlug * @return Response + * @internal param bool $pageSlug */ - public function create($bookSlug, $pageSlug = false) + public function create($bookSlug, $chapterSlug = false) { $book = $this->bookRepo->getBySlug($bookSlug); - $page = $pageSlug ? $this->pageRepo->getBySlug($pageSlug, $book->id) : false; - return view('pages/create', ['book' => $book, 'parentPage' => $page]); + $chapter = $chapterSlug ? $this->chapterRepo->getBySlug($chapterSlug, $book->id) : false; + return view('pages/create', ['book' => $book, 'chapter' => $chapter]); } /** @@ -67,15 +72,12 @@ class PageController extends Controller ]); $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->newFromInput($request->all()); - $slug = Str::slug($page->name); - while($this->pageRepo->countBySlug($slug, $book->id) > 0) { - $slug .= '1'; - } - $page->slug = $slug; + + $page->slug = $this->pageRepo->findSuitableSlug($page->name, $book->id); $page->priority = $this->bookRepo->getNewPriority($book); - if($request->has('parent')) { - $page->page_id = $request->get('parent'); + if($request->has('chapter') && $this->chapterRepo->idExists($request->get('chapter'))) { + $page->chapter_id = $request->get('chapter'); } $page->book_id = $book->id; @@ -95,9 +97,8 @@ class PageController extends Controller { $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->getBySlug($pageSlug, $book->id); - $breadCrumbs = $this->pageRepo->getBreadCrumbs($page); //dd($sidebarBookTree); - return view('pages/show', ['page' => $page, 'breadCrumbs' => $breadCrumbs, 'book' => $book]); + return view('pages/show', ['page' => $page, 'book' => $book]); } /** @@ -111,7 +112,7 @@ class PageController extends Controller { $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->getBySlug($pageSlug, $book->id); - return view('pages/edit', ['page' => $page]); + return view('pages/edit', ['page' => $page, 'book' => $book]); } /** @@ -127,10 +128,7 @@ class PageController extends Controller $book = $this->bookRepo->getBySlug($bookSlug); $page = $this->pageRepo->getBySlug($pageSlug, $book->id); $page->fill($request->all()); - $slug = Str::slug($page->name); - while($this->pageRepo->countBySlug($slug, $book->id) > 0 && $slug != $pageSlug) { - $slug .= '1'; - } + $page->slug = $this->pageRepo->findSuitableSlug($page->name, $book->id, $page->id); $page->text = strip_tags($page->html); $page->save(); return redirect($page->getUrl()); @@ -170,19 +168,36 @@ class PageController extends Controller public function sortPages($bookSlug) { $book = $this->bookRepo->getBySlug($bookSlug); - $tree = $this->bookRepo->getTree($book); - return view('pages/sort', ['book' => $book, 'tree' => $tree]); + return view('pages/sort', ['book' => $book]); } + /** + * Saves an array of sort mapping to pages and chapters. + * + * @param $bookSlug + * @param Request $request + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ public function savePageSort($bookSlug, Request $request) { $book = $this->bookRepo->getBySlug($bookSlug); + // Return if no map sent if(!$request->has('sort-tree')) { return redirect($book->getUrl()); } + // Sort pages and chapters $sortMap = json_decode($request->get('sort-tree')); - $this->pageRepo->applySortMap($sortMap, $book->id); + foreach($sortMap as $index => $bookChild) { + $id = $bookChild->id; + $isPage = $bookChild->type == 'page'; + $model = $isPage ? $this->pageRepo->getById($id) : $this->chapterRepo->getById($id); + $model->priority = $index; + if($isPage) { + $model->chapter_id = ($bookChild->parentChapter === false) ? 0 : $bookChild->parentChapter; + } + $model->save(); + } return redirect($book->getUrl()); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 06b802ae2..c6aa9a0bf 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -28,12 +28,12 @@ Route::group(['prefix' => 'books'], function() { Route::get('/{bookSlug}/sort', 'PageController@sortPages'); Route::put('/{bookSlug}/sort', 'PageController@savePageSort'); Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show'); - Route::get('/{bookSlug}/page/{pageSlug}/create', 'PageController@create'); Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit'); Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update'); Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy'); + Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create'); Route::get('/{bookSlug}/chapter/create', 'ChapterController@create'); Route::post('/{bookSlug}/chapter/create', 'ChapterController@store'); Route::get('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@show'); diff --git a/app/Page.php b/app/Page.php index 449cb15c4..cb54de442 100644 --- a/app/Page.php +++ b/app/Page.php @@ -22,6 +22,16 @@ class Page extends Model return $this->belongsTo('Oxbow\Book'); } + public function chapter() + { + return $this->belongsTo('Oxbow\Chapter'); + } + + public function hasChapter() + { + return $this->chapter()->count() > 0; + } + public function parent() { return $this->belongsTo('Oxbow\Page', 'page_id'); @@ -32,4 +42,9 @@ class Page extends Model return '/books/' . $this->book->slug . '/page/' . $this->slug; } + public function getExcerpt($length = 100) + { + return strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text; + } + } diff --git a/app/Repos/ChapterRepo.php b/app/Repos/ChapterRepo.php index 60d9b24a9..1eb819a82 100644 --- a/app/Repos/ChapterRepo.php +++ b/app/Repos/ChapterRepo.php @@ -18,6 +18,11 @@ class ChapterRepo $this->chapter = $chapter; } + public function idExists($id) + { + return $this->chapter->where('id', '=', $id)->count() > 0; + } + public function getById($id) { return $this->chapter->findOrFail($id); diff --git a/app/Repos/PageRepo.php b/app/Repos/PageRepo.php index 7ce10498d..99be07d7a 100644 --- a/app/Repos/PageRepo.php +++ b/app/Repos/PageRepo.php @@ -17,6 +17,11 @@ class PageRepo $this->page = $page; } + public function idExists($id) + { + return $this->page->where('page_id', '=', $id)->count() > 0; + } + public function getById($id) { return $this->page->findOrFail($id); @@ -59,41 +64,38 @@ class PageRepo return $query->get(); } - public function getBreadCrumbs($page) + /** + * Checks if a slug exists within a book already. + * @param $slug + * @param $bookId + * @param bool|false $currentId + * @return bool + */ + public function doesSlugExist($slug, $bookId, $currentId = false) { - $tree = []; - $cPage = $page; - while($cPage->parent && $cPage->parent->id !== 0) { - $cPage = $cPage->parent; - $tree[] = $cPage; + $query = $this->page->where('slug', '=', $slug)->where('book_id', '=', $bookId); + if($currentId) { + $query = $query->where('id', '!=', $currentId); } - return count($tree) > 0 ? array_reverse($tree) : false; + return $query->count() > 0; } /** - * Gets the pages at the top of the page hierarchy. + * Gets a suitable slug for the resource + * + * @param $name * @param $bookId + * @param bool|false $currentId + * @return string */ - private function getTopLevelPages($bookId) + public function findSuitableSlug($name, $bookId, $currentId = false) { - return $this->page->where('book_id', '=', $bookId)->where('chapter_id', '=', 0)->orderBy('priority')->get(); + $slug = Str::slug($name); + while($this->doesSlugExist($slug, $bookId, $currentId)) { + $slug .= '-' . substr(md5(rand(1, 500)), 0, 3); + } + return $slug; } - /** - * Applies a sort map to all applicable pages. - * @param $sortMap - * @param $bookId - */ - public function applySortMap($sortMap, $bookId) - { - foreach($sortMap as $index => $map) { - $page = $this->getById($map->id); - if($page->book_id === $bookId) { - $page->page_id = $map->parent; - $page->priority = $index; - $page->save(); - } - } - } } \ No newline at end of file diff --git a/bower.json b/bower.json index feed69959..7798e591a 100644 --- a/bower.json +++ b/bower.json @@ -17,6 +17,7 @@ "dropzone": "~4.0.1", "tinymce-dist": "~4.2.1", "bootstrap": "~3.3.5", - "jquery-sortable": "~0.9.13" + "jquery-sortable": "~0.9.13", + "material-design-iconic-font": "~2.1.1" } } diff --git a/resources/assets/sass/_buttons.scss b/resources/assets/sass/_buttons.scss index b28f29ea6..b37fae4fb 100644 --- a/resources/assets/sass/_buttons.scss +++ b/resources/assets/sass/_buttons.scss @@ -59,3 +59,30 @@ $button-border-radius: 3px; } } } + +// Floating action button +//.fab { +// $size: 70px; +// button.button { +// border-radius: 100%; +// width: $size; +// height: $size; +// font-size: 48px; +// text-align: center; +// margin: 0; +// padding: 0; +// border: 0; +// box-shadow: 0 0 2px 2px #DDD; +// transition: all ease-in-out 160ms; +// i { +// transform: rotate(0deg); +// transition: all ease-in-out 160ms; +// } +// &:hover { +// box-shadow: 0 2px 4px 2px #CCC; +// i { +// transform: rotate(180deg); +// } +// } +// } +//} diff --git a/resources/assets/sass/_text.scss b/resources/assets/sass/_text.scss index cf0649873..e286cc2ac 100644 --- a/resources/assets/sass/_text.scss +++ b/resources/assets/sass/_text.scss @@ -7,8 +7,8 @@ h1 { line-height: 1.22222222em; margin-top: 0.48888889em; margin-bottom: 0.48888889em; - padding-bottom: 0.3333em; - border-bottom: 1px solid #EAEAEA; + //padding-bottom: 0.3333em; + //border-bottom: 1px solid #EAEAEA; //margin-left: -$-xxl; //margin-right: -$-xxl; } @@ -73,7 +73,7 @@ hr { border: 0; height: 1px; border: 0; - background: #e3e0e0; + background: #EAEAEA; margin-bottom: $-l; &.faded { background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF); @@ -150,11 +150,17 @@ span.code { * Text colors */ p.pos, p .pos, span.pos, .text-pos { - color: $positive; + color: $positive; + &:hover { + color: $positive; + } } p.neg, p .neg, span.neg, .text-neg { - color: $negative; + color: $negative; + &:hover { + color: $negative; + } } p.muted, p .muted, span.muted, .text-muted { @@ -163,10 +169,16 @@ p.muted, p .muted, span.muted, .text-muted { p.primary, p .primary, span.primary, .text-primary { color: $primary; + &:hover { + color: $primary; + } } p.secondary, p .secondary, span.secondary, .text-secondary { color: $secondary; + &:hover { + color: $secondary; + } } /* diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss index 1b1318f92..461c7017e 100644 --- a/resources/assets/sass/styles.scss +++ b/resources/assets/sass/styles.scss @@ -67,7 +67,7 @@ ul.menu { li a { padding: $-m; display: block; - border-bottom: 1px solid #333; + border-bottom: 1px solid #3A3939; } } @@ -102,13 +102,21 @@ ul.menu { } .page-list { - a { + h3 { + margin: $-l 0; + } + .inset-list { display: block; - padding: $-s 0; - border-bottom: 2px dotted #CCC; - &:first-child { - border-top: 2px dotted #CCC; - } + overflow: hidden; + padding-left: $-l*2; + border-top: 3px dotted #EEE; + } + h4 { + display: block; + margin: $-m 0; + } + hr { + margin-top: 0; } } @@ -247,16 +255,20 @@ h1, h2, h3, h4, h5, h6 { padding-right: 4px; } span.sep { - color: #888; + color: #aaa; padding: 0 $-xs; } } .faded { - opacity: 0.5; - transition: opacity ease-in-out 120ms; - &:hover { - opacity: 1; + a { + color: #666; + opacity: 0.5; + transition: all ease-in-out 120ms; + &:hover { + opacity: 1; + text-decoration: none; + } } } @@ -277,68 +289,39 @@ h1, h2, h3, h4, h5, h6 { } -.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; - } - } -} - .book-tree h4 { padding: $-m $-s 0 $-s; i { padding-right: $-s; } } +// Sidebar list .book-tree .sidebar-page-list { list-style: none; margin: 0; li a { display: block; - padding: $-s $-m; - border-bottom: 2px dotted #333; + border-bottom: 1px solid #3A3939; } a.bold { color: #EEE !important; } + ul { + list-style: none; + margin: 0; + } + ul li a { + padding-left: $-xl; + } + .book { + color: #7BD06E !important; + } + .chapter { + color: #D2A64B !important; + } + .page { + color: #4599DC !important; + } } .sortable-page-list, .sortable-page-list ul { @@ -379,9 +362,4 @@ body.dragging, body.dragging * { } .sortable-page-list li.placeholder:before { position: absolute; -} - -.material-icons { - font-size: 1em; - line-height: 1.4em; } \ No newline at end of file diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php index 5fc10ce74..1c74f069a 100644 --- a/resources/views/base.blade.php +++ b/resources/views/base.blade.php @@ -5,7 +5,8 @@ - + {{----}} + @@ -42,8 +43,13 @@ + @if(isset($book) && !isset($books)) +
+ @include('pages/sidebar-tree-list', ['book' => $book]) +
+ @endif @yield('sidebar') diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php index 0cd1d82fd..3258e36de 100644 --- a/resources/views/books/index.blade.php +++ b/resources/views/books/index.blade.php @@ -7,7 +7,7 @@
diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index b5bb7c8d0..b4ae5cdb3 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -6,9 +6,11 @@
@@ -17,44 +19,45 @@

{{$book->name}}

{{$book->description}}

-
-

Contents

- -
- -
+
+
@foreach($book->children() as $childElement) -
+

@if(is_a($childElement, 'Oxbow\Chapter')) - + @else - + @endif {{ $childElement->name }}

+

+ {{$childElement->getExcerpt()}} +

+ + @if(is_a($childElement, 'Oxbow\Chapter')) +
+ @foreach($childElement->pages as $page) +

{{$page->name}}

+ @endforeach +
+ @endif

@endforeach
- {{--@include('pages/page-tree-list', ['pageTree' => $pageTree])--}} -