Showing
11 changed files
with
175 additions
and
4 deletions
| ... | @@ -135,12 +135,23 @@ class PageController extends Controller | ... | @@ -135,12 +135,23 @@ class PageController extends Controller |
| 135 | return redirect($page->getUrl()); | 135 | return redirect($page->getUrl()); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | + /** | ||
| 139 | + * Redirect from a special link url which | ||
| 140 | + * uses the page id rather than the name. | ||
| 141 | + * @param $pageId | ||
| 142 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||
| 143 | + */ | ||
| 138 | public function redirectFromLink($pageId) | 144 | public function redirectFromLink($pageId) |
| 139 | { | 145 | { |
| 140 | $page = $this->pageRepo->getById($pageId); | 146 | $page = $this->pageRepo->getById($pageId); |
| 141 | return redirect($page->getUrl()); | 147 | return redirect($page->getUrl()); |
| 142 | } | 148 | } |
| 143 | 149 | ||
| 150 | + /** | ||
| 151 | + * Search all available pages, Across all books. | ||
| 152 | + * @param Request $request | ||
| 153 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View | ||
| 154 | + */ | ||
| 144 | public function searchAll(Request $request) | 155 | public function searchAll(Request $request) |
| 145 | { | 156 | { |
| 146 | $searchTerm = $request->get('term'); | 157 | $searchTerm = $request->get('term'); |
| ... | @@ -151,6 +162,30 @@ class PageController extends Controller | ... | @@ -151,6 +162,30 @@ class PageController extends Controller |
| 151 | } | 162 | } |
| 152 | 163 | ||
| 153 | /** | 164 | /** |
| 165 | + * Shows the view which allows pages to be re-ordered and sorted. | ||
| 166 | + * @param $bookSlug | ||
| 167 | + * @return \Illuminate\View\View | ||
| 168 | + */ | ||
| 169 | + public function sortPages($bookSlug) | ||
| 170 | + { | ||
| 171 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 172 | + $tree = $this->bookRepo->getTree($book); | ||
| 173 | + return view('pages/sort', ['book' => $book, 'tree' => $tree]); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + public function savePageSort($bookSlug, Request $request) | ||
| 177 | + { | ||
| 178 | + $book = $this->bookRepo->getBySlug($bookSlug); | ||
| 179 | + if(!$request->has('sort-tree')) { | ||
| 180 | + return redirect($book->getUrl()); | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + $sortMap = json_decode($request->get('sort-tree')); | ||
| 184 | + $this->pageRepo->applySortMap($sortMap, $book->id); | ||
| 185 | + return redirect($book->getUrl()); | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + /** | ||
| 154 | * Remove the specified resource from storage. | 189 | * Remove the specified resource from storage. |
| 155 | * | 190 | * |
| 156 | * @param int $id | 191 | * @param int $id | ... | ... |
| ... | @@ -24,6 +24,8 @@ Route::group(['prefix' => 'books'], function() { | ... | @@ -24,6 +24,8 @@ Route::group(['prefix' => 'books'], function() { |
| 24 | 24 | ||
| 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}/sort', 'PageController@sortPages'); | ||
| 28 | + Route::put('/{bookSlug}/sort', 'PageController@savePageSort'); | ||
| 27 | Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); | 29 | Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); |
| 28 | Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create'); | 30 | Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create'); |
| 29 | Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); | 31 | Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); | ... | ... |
| ... | @@ -24,7 +24,7 @@ class Page extends Model | ... | @@ -24,7 +24,7 @@ class Page extends Model |
| 24 | 24 | ||
| 25 | public function children() | 25 | public function children() |
| 26 | { | 26 | { |
| 27 | - return $this->hasMany('Oxbow\Page'); | 27 | + return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC'); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | public function parent() | 30 | public function parent() | ... | ... |
| ... | @@ -57,6 +57,7 @@ class BookRepo | ... | @@ -57,6 +57,7 @@ class BookRepo |
| 57 | { | 57 | { |
| 58 | $tree = $book->toArray(); | 58 | $tree = $book->toArray(); |
| 59 | $tree['pages'] = $this->pageRepo->getTreeByBookId($book->id); | 59 | $tree['pages'] = $this->pageRepo->getTreeByBookId($book->id); |
| 60 | + $tree['hasChildren'] = count($tree['pages']) > 0; | ||
| 60 | return $tree; | 61 | return $tree; |
| 61 | } | 62 | } |
| 62 | 63 | ... | ... |
| ... | @@ -114,7 +114,24 @@ class PageRepo | ... | @@ -114,7 +114,24 @@ class PageRepo |
| 114 | */ | 114 | */ |
| 115 | private function getTopLevelPages($bookId) | 115 | private function getTopLevelPages($bookId) |
| 116 | { | 116 | { |
| 117 | - return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->get(); | 117 | + return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->orderBy('priority')->get(); |
| 118 | + } | ||
| 119 | + | ||
| 120 | + /** | ||
| 121 | + * Applies a sort map to all applicable pages. | ||
| 122 | + * @param $sortMap | ||
| 123 | + * @param $bookId | ||
| 124 | + */ | ||
| 125 | + public function applySortMap($sortMap, $bookId) | ||
| 126 | + { | ||
| 127 | + foreach($sortMap as $index => $map) { | ||
| 128 | + $page = $this->getById($map->id); | ||
| 129 | + if($page->book_id === $bookId) { | ||
| 130 | + $page->page_id = $map->parent; | ||
| 131 | + $page->priority = $index; | ||
| 132 | + $page->save(); | ||
| 133 | + } | ||
| 134 | + } | ||
| 118 | } | 135 | } |
| 119 | 136 | ||
| 120 | } | 137 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -94,6 +94,9 @@ header .menu { | ... | @@ -94,6 +94,9 @@ header .menu { |
| 94 | &:hover { | 94 | &:hover { |
| 95 | opacity: 1; | 95 | opacity: 1; |
| 96 | } | 96 | } |
| 97 | + .buttons a { | ||
| 98 | + display: block; | ||
| 99 | + } | ||
| 97 | } | 100 | } |
| 98 | 101 | ||
| 99 | .page-nav-list { | 102 | .page-nav-list { |
| ... | @@ -268,4 +271,44 @@ h1, h2, h3, h4, h5, h6 { | ... | @@ -268,4 +271,44 @@ h1, h2, h3, h4, h5, h6 { |
| 268 | display: block; | 271 | display: block; |
| 269 | } | 272 | } |
| 270 | } | 273 | } |
| 274 | +} | ||
| 275 | + | ||
| 276 | +.sortable-page-list, .sortable-page-list ul { | ||
| 277 | + list-style: none; | ||
| 278 | + //background-color: rgba(0, 0, 0, 0.04); | ||
| 279 | +} | ||
| 280 | +.sortable-page-list { | ||
| 281 | + margin-left: 0; | ||
| 282 | + ul { | ||
| 283 | + margin-bottom: 0; | ||
| 284 | + margin-top: 0; | ||
| 285 | + } | ||
| 286 | + li { | ||
| 287 | + border-bottom: 1px solid #BBB; | ||
| 288 | + border-left: 1px solid #BBB; | ||
| 289 | + border-right: 1px solid #BBB; | ||
| 290 | + padding: $-xs $-s; | ||
| 291 | + } | ||
| 292 | + li:first-child { | ||
| 293 | + margin-top: $-xs; | ||
| 294 | + border-top: 1px solid #BBB; | ||
| 295 | + } | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +// Jquery Sortable Styles | ||
| 299 | +.dragged { | ||
| 300 | + position: absolute; | ||
| 301 | + opacity: 0.5; | ||
| 302 | + z-index: 2000; | ||
| 303 | +} | ||
| 304 | + | ||
| 305 | +body.dragging, body.dragging * { | ||
| 306 | + cursor: move !important; | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +.sortable-page-list li.placeholder { | ||
| 310 | + position: relative; | ||
| 311 | +} | ||
| 312 | +.sortable-page-list li.placeholder:before { | ||
| 313 | + position: absolute; | ||
| 271 | } | 314 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -8,6 +8,7 @@ | ... | @@ -8,6 +8,7 @@ |
| 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 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> | 9 | <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> | 10 | <script src="/bower/bootstrap/dist/js/bootstrap.js"></script> |
| 11 | + <script src="/bower/jquery-sortable/source/js/jquery-sortable.js"></script> | ||
| 11 | <script> | 12 | <script> |
| 12 | $.fn.smoothScrollTo = function() { | 13 | $.fn.smoothScrollTo = function() { |
| 13 | if(this.length === 0) return; | 14 | if(this.length === 0) return; | ... | ... |
| ... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
| 9 | <h4>Book Actions</h4> | 9 | <h4>Book Actions</h4> |
| 10 | <div class="buttons"> | 10 | <div class="buttons"> |
| 11 | <a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit Book</a> | 11 | <a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit Book</a> |
| 12 | + <a href="{{ $book->getUrl() }}/sort"><i class="fa fa-sort"></i>Sort Pages</a> | ||
| 12 | </div> | 13 | </div> |
| 13 | </div> | 14 | </div> |
| 14 | 15 | ... | ... |
| ... | @@ -28,10 +28,10 @@ | ... | @@ -28,10 +28,10 @@ |
| 28 | @endif | 28 | @endif |
| 29 | </div> | 29 | </div> |
| 30 | <h1>{{$page->name}}</h1> | 30 | <h1>{{$page->name}}</h1> |
| 31 | - @if(count($page->pages) > 0) | 31 | + @if(count($page->children) > 0) |
| 32 | <h4 class="text-muted">Sub-pages</h4> | 32 | <h4 class="text-muted">Sub-pages</h4> |
| 33 | <div class="page-list"> | 33 | <div class="page-list"> |
| 34 | - @foreach($page->pages as $childPage) | 34 | + @foreach($page->children as $childPage) |
| 35 | <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a> | 35 | <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a> |
| 36 | @endforeach | 36 | @endforeach |
| 37 | </div> | 37 | </div> | ... | ... |
resources/views/pages/sort.blade.php
0 → 100644
| 1 | +@extends('base') | ||
| 2 | + | ||
| 3 | +@section('content') | ||
| 4 | + | ||
| 5 | + <div class="row"> | ||
| 6 | + <div class="page-menu col-md-3"> | ||
| 7 | + <div class="page-actions"> | ||
| 8 | + <form action="{{$book->getUrl()}}/sort" method="POST"> | ||
| 9 | + {!! csrf_field() !!} | ||
| 10 | + <input type="hidden" name="_method" value="PUT"> | ||
| 11 | + <input type="hidden" id="sort-tree-input" name="sort-tree"> | ||
| 12 | + <h4>Actions</h4> | ||
| 13 | + <div class="list"> | ||
| 14 | + <button class="button pos" type="submit">Save Ordering</button> | ||
| 15 | + </div> | ||
| 16 | + </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 | + | ||
| 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> | ||
| 31 | + | ||
| 32 | + <script> | ||
| 33 | + $(document).ready(function() { | ||
| 34 | + | ||
| 35 | + var group = $('#sort-list').sortable({ | ||
| 36 | + group: 'serialization', | ||
| 37 | + onDrop: function($item, container, _super) { | ||
| 38 | + var data = group.sortable('serialize').get(); | ||
| 39 | + console.log(data); | ||
| 40 | + var pageMap = []; | ||
| 41 | + var parent = 0; | ||
| 42 | + buildPageMap(pageMap, parent, data[0]); | ||
| 43 | + $('#sort-tree-input').val(JSON.stringify(pageMap)); | ||
| 44 | + _super($item, container); | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + | ||
| 48 | + function buildPageMap(pageMap, parent, data) { | ||
| 49 | + for(var i = 0; i < data.length; i++) { | ||
| 50 | + var page = data[i]; | ||
| 51 | + pageMap.push({ | ||
| 52 | + id: page.id, | ||
| 53 | + parent: parent | ||
| 54 | + }); | ||
| 55 | + buildPageMap(pageMap, page.id, page.children[0]); | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + }); | ||
| 60 | + </script> | ||
| 61 | +@stop |
-
Please register or sign in to post a comment