mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Made chapters functional and cleaned design features
This commit is contained in:
parent
fd1a0dceb2
commit
b54ef3bb4b
@ -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');
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
15
app/Page.php
15
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
@ -5,7 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="stylesheet" href="/css/app.css">
|
||||
<link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||
{{--<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">--}}
|
||||
<link rel="stylesheet" href="/bower/material-design-iconic-font/dist/css/material-design-iconic-font.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>
|
||||
@ -42,8 +43,13 @@
|
||||
</form>
|
||||
</div>
|
||||
<ul class="menu">
|
||||
<li><a href="/books"><i class="fa fa-book"></i>Books</a></li>
|
||||
<li><a href="/books"><i class="zmdi zmdi-book"></i>Books</a></li>
|
||||
</ul>
|
||||
@if(isset($book) && !isset($books))
|
||||
<div class="book-tree">
|
||||
@include('pages/sidebar-tree-list', ['book' => $book])
|
||||
</div>
|
||||
@endif
|
||||
@yield('sidebar')
|
||||
</section>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class="col-md-6"></div>
|
||||
<div class="col-md-6 faded">
|
||||
<div class="action-buttons">
|
||||
<a href="/books/create">+ Add new book</a>
|
||||
<a href="/books/create" class="text-pos"><i class="zmdi zmdi-plus"></i>Add new book</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,9 +6,11 @@
|
||||
<div class="col-md-6"></div>
|
||||
<div class="col-md-6">
|
||||
<div class="action-buttons faded">
|
||||
<a href="{{$book->getEditUrl()}}"><i class="fa fa-pencil"></i>Edit</a>
|
||||
<a href="{{ $book->getUrl() }}/sort"><i class="fa fa-sort"></i>Sort</a>
|
||||
<a href="{{ $book->getUrl() }}/delete"><i class="fa fa-trash"></i>Delete</a>
|
||||
<a href="{{$book->getUrl() . '/page/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Page</a>
|
||||
<a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos"><i class="zmdi zmdi-plus"></i> New Chapter</a>
|
||||
<a href="{{$book->getEditUrl()}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
<a href="{{ $book->getUrl() }}/sort" class="text-primary"><i class="zmdi zmdi-sort"></i>Sort</a>
|
||||
<a href="{{ $book->getUrl() }}/delete" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -17,44 +19,45 @@
|
||||
<h1>{{$book->name}}</h1>
|
||||
<p class="text-muted">{{$book->description}}</p>
|
||||
|
||||
<div class="clearfix header-group">
|
||||
<h4 class="float">Contents</h4>
|
||||
<div class="float right">
|
||||
<a href="{{$book->getUrl() . '/page/create'}}" class="text-pos">+ New Page</a>
|
||||
<a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos">+ New Chapter</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="page-list">
|
||||
<hr>
|
||||
@foreach($book->children() as $childElement)
|
||||
<div >
|
||||
<div class="book-child">
|
||||
<h3>
|
||||
<a href="{{ $childElement->getUrl() }}">
|
||||
@if(is_a($childElement, 'Oxbow\Chapter'))
|
||||
<i class="fa fa-archive"></i>
|
||||
<i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
|
||||
@else
|
||||
<i class="fa fa-file"></i>
|
||||
<i class="zmdi zmdi-file-text"></i>
|
||||
@endif
|
||||
{{ $childElement->name }}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="text-muted">
|
||||
{{$childElement->getExcerpt()}}
|
||||
</p>
|
||||
|
||||
@if(is_a($childElement, 'Oxbow\Chapter'))
|
||||
<div class="inset-list">
|
||||
@foreach($childElement->pages as $page)
|
||||
<h4><a href="{{$page->getUrl()}}"><i class="zmdi zmdi-file-text"></i> {{$page->name}}</a></h4>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<hr>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{--@include('pages/page-tree-list', ['pageTree' => $pageTree])--}}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
|
||||
$('.nested-page-list i.arrow').click(function() {
|
||||
var list = $(this).closest('.nested-page-list');
|
||||
var listItem = $(this).closest('li');
|
||||
listItem.toggleClass('expanded');
|
||||
$('.chapter-toggle').click(function(e) {
|
||||
e.preventDefault();
|
||||
$(this).closest('.book-child').find('.inset-list').slideToggle(180);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -3,11 +3,16 @@
|
||||
@section('content')
|
||||
|
||||
<div class="row faded-small">
|
||||
<div class="col-md-6"></div>
|
||||
<div class="col-md-6 faded">
|
||||
<div class="breadcrumbs padded-horizontal">
|
||||
<a href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 faded">
|
||||
<div class="action-buttons">
|
||||
<a href="{{$chapter->getUrl() . '/edit'}}" ><i class="fa fa-pencil"></i>Edit</a>
|
||||
<a href="{{$chapter->getUrl() . '/delete'}}"><i class="fa fa-trash"></i>Delete</a>
|
||||
<a href="{{$chapter->getUrl() . '/create-page'}}" class="text-pos"><i class="zmdi zmdi-plus"></i>New Page</a>
|
||||
<a href="{{$chapter->getUrl() . '/edit'}}" class="text-primary"><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
<a href="{{$chapter->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -16,21 +21,26 @@
|
||||
<div class="page-content">
|
||||
<h1>{{ $chapter->name }}</h1>
|
||||
<p class="text-muted">{{ $chapter->description }}</p>
|
||||
|
||||
@if(count($chapter->pages) > 0)
|
||||
<h4 class="text-muted">Pages</h4>
|
||||
<div class="page-list">
|
||||
<hr>
|
||||
@foreach($chapter->pages as $page)
|
||||
<div >
|
||||
<h3>
|
||||
<a href="{{ $page->getUrl() }}">
|
||||
<i class="fa fa-file"></i>
|
||||
{{ $page->name }}
|
||||
<i class="zmdi zmdi-file-text"></i>{{ $page->name }}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="text-muted">
|
||||
{{$page->getExcerpt()}}
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">No pages are in this chapter</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
@section('content')
|
||||
<form action="{{$book->getUrl() . '/page'}}" method="POST">
|
||||
@include('pages/form')
|
||||
@if($parentPage)
|
||||
<input type="hidden" name="parent" value="{{$parentPage->id}}">
|
||||
@if($chapter)
|
||||
<input type="hidden" name="chapter" value="{{$chapter->id}}">
|
||||
@endif
|
||||
</form>
|
||||
@stop
|
||||
|
@ -1,15 +0,0 @@
|
||||
{{--Requires an array of pages to be passed as $pageTree--}}
|
||||
|
||||
<ul class="nested-page-list">
|
||||
@foreach($pageTree as $subPage)
|
||||
<li @if($subPage['hasChildren'])class="has-children"@endif>
|
||||
@if($subPage['hasChildren'])
|
||||
<i class="fa fa-chevron-right arrow"></i>
|
||||
@endif
|
||||
<a href="{{$subPage['url']}}">{{$subPage['name']}}</a>
|
||||
@if($subPage['hasChildren'])
|
||||
@include('pages/page-tree-list', ['pageTree' => $subPage['pages']])
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
@ -1,10 +0,0 @@
|
||||
|
||||
<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>
|
@ -1,30 +1,24 @@
|
||||
@extends('base')
|
||||
|
||||
@section('sidebar')
|
||||
<div class="book-tree">
|
||||
<h4><a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{$book->name}}</a></h4>
|
||||
@include('pages/sidebar-tree-list', ['book' => $book])
|
||||
</div>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row faded-small">
|
||||
<div class="col-md-6 faded">
|
||||
<div class="breadcrumbs padded-horizontal">
|
||||
<a href="{{$book->getUrl()}}"><i class="fa fa-book"></i>{{ $book->name }}</a>
|
||||
@if($breadCrumbs)
|
||||
@foreach($breadCrumbs as $parentPage)
|
||||
<span class="sep">></span>
|
||||
<a href="{{$parentPage->getUrl()}}">{{ $parentPage->name }}</a>
|
||||
@endforeach
|
||||
<a href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{ $book->name }}</a>
|
||||
@if($page->hasChapter())
|
||||
<span class="sep">»</span>
|
||||
<a href="{{ $page->chapter->getUrl() }}">
|
||||
<i class="zmdi zmdi-collection-bookmark"></i>
|
||||
{{$page->chapter->name}}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 faded">
|
||||
<div class="action-buttons">
|
||||
<a href="{{$page->getUrl() . '/edit'}}" ><i class="fa fa-pencil"></i>Edit</a>
|
||||
<a href="{{$page->getUrl() . '/delete'}}"><i class="fa fa-trash"></i>Delete</a>
|
||||
<a href="{{$page->getUrl() . '/edit'}}" class="text-primary" ><i class="zmdi zmdi-edit"></i>Edit</a>
|
||||
<a href="{{$page->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,18 +51,23 @@
|
||||
var pageNav = $('.page-nav-list');
|
||||
var pageContent = $('.page-content');
|
||||
var headers = pageContent.find('h1, h2, h3, h4, h5, h6');
|
||||
headers.each(function() {
|
||||
var header = $(this);
|
||||
var tag = header.prop('tagName');
|
||||
var listElem = $('<li></li>').addClass('nav-'+tag);
|
||||
var link = $('<a></a>').text(header.text().trim()).attr('href', '#');
|
||||
listElem.append(link);
|
||||
pageNav.append(listElem);
|
||||
link.click(function(e) {
|
||||
e.preventDefault();
|
||||
header.smoothScrollTo();
|
||||
})
|
||||
});
|
||||
if(headers.length > 5) {
|
||||
headers.each(function() {
|
||||
var header = $(this);
|
||||
var tag = header.prop('tagName');
|
||||
var listElem = $('<li></li>').addClass('nav-'+tag);
|
||||
var link = $('<a></a>').text(header.text().trim()).attr('href', '#');
|
||||
listElem.append(link);
|
||||
pageNav.append(listElem);
|
||||
link.click(function(e) {
|
||||
e.preventDefault();
|
||||
header.smoothScrollTo();
|
||||
})
|
||||
});
|
||||
} else {
|
||||
$('.side-nav').hide();
|
||||
}
|
||||
|
||||
|
||||
// Set up link hooks
|
||||
var pageId = {{$page->id}};
|
||||
|
@ -1,13 +1,30 @@
|
||||
{{--Requires an array of pages to be passed as $pageTree--}}
|
||||
|
||||
<ul class="sidebar-page-list">
|
||||
<ul class="sidebar-page-list menu">
|
||||
<li class="book-header"><a href="{{$book->getUrl()}}" class="book"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></li>
|
||||
@foreach($book->children() as $bookChild)
|
||||
<li>
|
||||
{{ $bookChild->name }}
|
||||
<a href="{{$bookChild->getUrl()}}" class="@if(is_a($bookChild, 'Oxbow\Chapter')) chapter @else page @endif">
|
||||
@if(is_a($bookChild, 'Oxbow\Chapter'))
|
||||
<i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
|
||||
@else
|
||||
<i class="zmdi zmdi-file-text"></i>
|
||||
@endif
|
||||
{{ $bookChild->name }}
|
||||
</a>
|
||||
|
||||
@if(is_a($bookChild, 'Oxbow\Chapter') && count($bookChild->pages) > 0)
|
||||
<ul>
|
||||
@foreach($pages as $page)
|
||||
<li>{{ $page->name }}</li>
|
||||
<ul class="menu">
|
||||
@foreach($bookChild->pages as $page)
|
||||
<li>
|
||||
<a href="{{$page->getUrl()}}" class="@if(is_a($page, 'Oxbow\Chapter')) chapter @else page @endif">
|
||||
@if(is_a($page, 'Oxbow\Chapter'))
|
||||
<i class="zmdi zmdi-collection-bookmark chapter-toggle"></i>
|
||||
@else
|
||||
<i class="zmdi zmdi-file-text"></i>
|
||||
@endif
|
||||
{{ $page->name }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
|
@ -2,31 +2,35 @@
|
||||
|
||||
@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 class="page-content">
|
||||
<h1>{{ $book->name }} <span class="subheader">Sort Pages</span></h1>
|
||||
|
||||
<ul class="sortable-page-list" id="sort-list">
|
||||
@foreach($book->children() as $bookChild)
|
||||
<li data-id="{{$bookChild->id}}" data-type="{{ is_a($bookChild, 'Oxbow\Chapter') ? 'chapter' : 'page' }}">
|
||||
{{ $bookChild->name }}
|
||||
@if(is_a($bookChild, 'Oxbow\Chapter'))
|
||||
<ul>
|
||||
@foreach($bookChild->pages as $page)
|
||||
<li data-id="{{$page->id}}" data-type="page">
|
||||
{{ $page->name }}
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
<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">
|
||||
<div class="list">
|
||||
<button class="button pos" type="submit">Save Ordering</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<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>
|
||||
@ -36,24 +40,35 @@
|
||||
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]);
|
||||
var pageMap = buildPageMap(data[0]);
|
||||
$('#sort-tree-input').val(JSON.stringify(pageMap));
|
||||
_super($item, container);
|
||||
}
|
||||
});
|
||||
|
||||
function buildPageMap(pageMap, parent, data) {
|
||||
function buildPageMap(data) {
|
||||
var pageMap = [];
|
||||
for(var i = 0; i < data.length; i++) {
|
||||
var page = data[i];
|
||||
var bookChild = data[i];
|
||||
pageMap.push({
|
||||
id: page.id,
|
||||
parent: parent
|
||||
id: bookChild.id,
|
||||
parentChapter: false,
|
||||
type: bookChild.type
|
||||
});
|
||||
buildPageMap(pageMap, page.id, page.children[0]);
|
||||
if(bookChild.type == 'chapter' && bookChild.children) {
|
||||
var chapterId = bookChild.id;
|
||||
var chapterChildren = bookChild.children[0];
|
||||
for(var j = 0; j < chapterChildren.length; j++) {
|
||||
var page = chapterChildren[j];
|
||||
pageMap.push({
|
||||
id: page.id,
|
||||
parentChapter: chapterId,
|
||||
type: 'page'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return pageMap;
|
||||
}
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user