]> BookStack Code Mirror - bookstack/commitdiff
Addded sorting logic to pages
authorDan Brown <redacted>
Tue, 21 Jul 2015 19:13:29 +0000 (20:13 +0100)
committerDan Brown <redacted>
Tue, 21 Jul 2015 19:13:29 +0000 (20:13 +0100)
app/Http/Controllers/PageController.php
app/Http/routes.php
app/Page.php
app/Repos/BookRepo.php
app/Repos/PageRepo.php
resources/assets/sass/styles.scss
resources/views/base.blade.php
resources/views/books/show.blade.php
resources/views/pages/page-tree-sort.blade.php [new file with mode: 0644]
resources/views/pages/show.blade.php
resources/views/pages/sort.blade.php [new file with mode: 0644]

index f2a137429dab058e5f3c418cd796d441a1a25943..19e2011838283a091aff063d1f36ef802be34205 100644 (file)
@@ -135,12 +135,23 @@ class PageController extends Controller
         return redirect($page->getUrl());
     }
 
+    /**
+     * Redirect from a special link url which
+     * uses the page id rather than the name.
+     * @param $pageId
+     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     */
     public function redirectFromLink($pageId)
     {
         $page = $this->pageRepo->getById($pageId);
         return redirect($page->getUrl());
     }
 
+    /**
+     * Search all available pages, Across all books.
+     * @param Request $request
+     * @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
+     */
     public function searchAll(Request $request)
     {
         $searchTerm = $request->get('term');
@@ -150,6 +161,30 @@ class PageController extends Controller
         return view('pages/search-results', ['pages' => $pages, 'searchTerm' => $searchTerm]);
     }
 
+    /**
+     * Shows the view which allows pages to be re-ordered and sorted.
+     * @param $bookSlug
+     * @return \Illuminate\View\View
+     */
+    public function sortPages($bookSlug)
+    {
+        $book = $this->bookRepo->getBySlug($bookSlug);
+        $tree = $this->bookRepo->getTree($book);
+        return view('pages/sort', ['book' => $book, 'tree' => $tree]);
+    }
+
+    public function savePageSort($bookSlug, Request $request)
+    {
+        $book = $this->bookRepo->getBySlug($bookSlug);
+        if(!$request->has('sort-tree')) {
+            return redirect($book->getUrl());
+        }
+
+        $sortMap = json_decode($request->get('sort-tree'));
+        $this->pageRepo->applySortMap($sortMap, $book->id);
+        return redirect($book->getUrl());
+    }
+
     /**
      * Remove the specified resource from storage.
      *
index e80f53274a5049d6ac1911abccf6f4e60a62562d..0dd0226a2bed5a140039f4483ce4a34695be8d52 100644 (file)
@@ -24,6 +24,8 @@ Route::group(['prefix' => 'books'], function() {
 
     Route::get('/{bookSlug}/page/create', 'PageController@create');
     Route::post('/{bookSlug}/page', 'PageController@store');
+    Route::get('/{bookSlug}/sort', 'PageController@sortPages');
+    Route::put('/{bookSlug}/sort', 'PageController@savePageSort');
     Route::get('/{bookSlug}/{pageSlug}', 'PageController@show');
     Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create');
     Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit');
index 3d9e6e3b2af9789c48be6c90a1bf72b56850ac30..819b14024671c2f1158f7b0c0d9f3bd678823106 100644 (file)
@@ -24,7 +24,7 @@ class Page extends Model
 
     public function children()
     {
-        return $this->hasMany('Oxbow\Page');
+        return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC');
     }
 
     public function parent()
index 3693fe40acf2cb24485d452c46f65d53aaa89443..060b6fc49645fd5f9ad191f49c1ee8205a47556d 100644 (file)
@@ -57,6 +57,7 @@ class BookRepo
     {
         $tree = $book->toArray();
         $tree['pages'] = $this->pageRepo->getTreeByBookId($book->id);
+        $tree['hasChildren'] = count($tree['pages']) > 0;
         return $tree;
     }
 
index 08da6654ba0481fa73ffaf12237975680e13b858..05fe3f7073f1139ebb73ee4d40376c54c571a1a0 100644 (file)
@@ -114,7 +114,24 @@ class PageRepo
      */
     private function getTopLevelPages($bookId)
     {
-        return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->get();
+        return $this->page->where('book_id', '=', $bookId)->where('page_id', '=', 0)->orderBy('priority')->get();
+    }
+
+    /**
+     * 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
index ef9a69bc3a902095762c656788f963c95e1754ce..0fd4f638d6d7317f6e0f1d8fc460c9f8a077573b 100644 (file)
@@ -94,6 +94,9 @@ header .menu {
   &:hover {
     opacity: 1;
   }
+  .buttons a {
+    display: block;
+  }
 }
 
 .page-nav-list {
@@ -268,4 +271,44 @@ h1, h2, h3, h4, h5, h6 {
       display: block;
     }
   }
+}
+
+.sortable-page-list, .sortable-page-list ul {
+  list-style: none;
+  //background-color: rgba(0, 0, 0, 0.04);
+}
+.sortable-page-list {
+  margin-left: 0;
+  ul {
+    margin-bottom: 0;
+    margin-top: 0;
+  }
+  li {
+    border-bottom: 1px solid #BBB;
+    border-left: 1px solid #BBB;
+    border-right: 1px solid #BBB;
+    padding: $-xs $-s;
+  }
+  li:first-child {
+    margin-top: $-xs;
+    border-top: 1px solid #BBB;
+  }
+}
+
+// Jquery Sortable Styles
+.dragged {
+  position: absolute;
+  opacity: 0.5;
+  z-index: 2000;
+}
+
+body.dragging, body.dragging * {
+  cursor: move !important;
+}
+
+.sortable-page-list li.placeholder {
+  position: relative;
+}
+.sortable-page-list li.placeholder:before {
+  position: absolute;
 }
\ No newline at end of file
index 59860b03764106027eb01614967c17725738f135..282b0d35e659702689303a2e477a0fa0a0588c2b 100644 (file)
@@ -8,6 +8,7 @@
     <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
     <script src="/bower/bootstrap/dist/js/bootstrap.js"></script>
+    <script src="/bower/jquery-sortable/source/js/jquery-sortable.js"></script>
     <script>
         $.fn.smoothScrollTo = function() {
             if(this.length === 0) return;
index f562655f803edb4cc91b53dbd2b88975ae8ad50f..6b0ed62358bb0e8a8a946d57bdb2ad69d4084d08 100644 (file)
@@ -9,6 +9,7 @@
             <h4>Book Actions</h4>
             <div class="buttons">
                 <a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit Book</a>
+                <a href="{{ $book->getUrl() }}/sort"><i class="fa fa-sort"></i>Sort Pages</a>
             </div>
         </div>
 
diff --git a/resources/views/pages/page-tree-sort.blade.php b/resources/views/pages/page-tree-sort.blade.php
new file mode 100644 (file)
index 0000000..4980be9
--- /dev/null
@@ -0,0 +1,10 @@
+
+<li data-id="{{$page['id']}}">{{ $page['name'] }}
+    <ul>
+        @if($page['hasChildren'])
+            @foreach($page['pages'] as $childPage)
+                @include('pages/page-tree-sort', ['page'=>$childPage])
+            @endforeach
+        @endif
+    </ul>
+</li>
index 0a957fc5e2d9e68978aa411e9a45f90c8a526563..a5eac3c98172093f20e7717068ab4e8cb203bd90 100644 (file)
                 @endif
             </div>
             <h1>{{$page->name}}</h1>
-            @if(count($page->pages) > 0)
+            @if(count($page->children) > 0)
                 <h4 class="text-muted">Sub-pages</h4>
                 <div class="page-list">
-                    @foreach($page->pages as $childPage)
+                    @foreach($page->children as $childPage)
                         <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a>
                     @endforeach
                 </div>
diff --git a/resources/views/pages/sort.blade.php b/resources/views/pages/sort.blade.php
new file mode 100644 (file)
index 0000000..98a42e6
--- /dev/null
@@ -0,0 +1,61 @@
+@extends('base')
+
+@section('content')
+
+    <div class="row">
+        <div class="page-menu col-md-3">
+            <div class="page-actions">
+                <form action="{{$book->getUrl()}}/sort" method="POST">
+                    {!! csrf_field() !!}
+                    <input type="hidden" name="_method" value="PUT">
+                    <input type="hidden" id="sort-tree-input" name="sort-tree">
+                    <h4>Actions</h4>
+                    <div class="list">
+                        <button class="button pos" type="submit">Save Ordering</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+
+        <div class="page-content right col-md-9">
+            <h1>{{ $book->name }} <span class="subheader">Sort Pages</span></h1>
+
+            <ul class="sortable-page-list" id="sort-list">
+                @foreach($tree['pages'] as $treePage)
+                    @include('pages/page-tree-sort', ['page' => $treePage])
+                @endforeach
+            </ul>
+
+        </div>
+    </div>
+
+    <script>
+        $(document).ready(function() {
+
+            var group = $('#sort-list').sortable({
+                group: 'serialization',
+                onDrop: function($item, container, _super) {
+                    var data = group.sortable('serialize').get();
+                    console.log(data);
+                    var pageMap = [];
+                    var parent = 0;
+                    buildPageMap(pageMap, parent, data[0]);
+                    $('#sort-tree-input').val(JSON.stringify(pageMap));
+                    _super($item, container);
+                }
+            });
+
+            function buildPageMap(pageMap, parent, data) {
+                for(var i = 0; i < data.length; i++) {
+                    var page = data[i];
+                    pageMap.push({
+                        id: page.id,
+                        parent: parent
+                    });
+                    buildPageMap(pageMap, page.id, page.children[0]);
+                }
+            }
+
+        });
+    </script>
+@stop