Dan Brown

Started working on chapters

...@@ -24,4 +24,17 @@ class Book extends Model ...@@ -24,4 +24,17 @@ class Book extends Model
24 return $this->hasMany('Oxbow\Page'); 24 return $this->hasMany('Oxbow\Page');
25 } 25 }
26 26
27 + public function chapters()
28 + {
29 + return $this->hasMany('Oxbow\Chapter');
30 + }
31 +
32 + public function children()
33 + {
34 + $pages = $this->pages()->get();
35 + $chapters = $this->chapters()->get();
36 + $children = $pages->merge($chapters);
37 + return $children->sortBy('priority');
38 + }
39 +
27 } 40 }
......
1 +<?php namespace Oxbow;
2 +
3 +use Illuminate\Database\Eloquent\Model;
4 +
5 +class Chapter extends Model
6 +{
7 +
8 + protected $fillable = ['name', 'description', 'priority', 'book_id'];
9 +
10 + public function book()
11 + {
12 + return $this->belongsTo('Oxbow\Book');
13 + }
14 +
15 + public function children()
16 + {
17 + return $this->hasMany('Oxbow\Page')->orderBy('priority', 'ASC');
18 + }
19 +
20 + public function getUrl()
21 + {
22 + return '/books/' . $this->book->slug . '/chapter/' . $this->slug;
23 + }
24 +
25 +}
...@@ -79,7 +79,6 @@ class BookController extends Controller ...@@ -79,7 +79,6 @@ class BookController extends Controller
79 { 79 {
80 $book = $this->bookRepo->getBySlug($slug); 80 $book = $this->bookRepo->getBySlug($slug);
81 $pageTree = $this->pageRepo->getTreeByBookId($book->id); 81 $pageTree = $this->pageRepo->getTreeByBookId($book->id);
82 - // dd($pageTree);
83 return view('books/show', ['book' => $book, 'pageTree' => $pageTree]); 82 return view('books/show', ['book' => $book, 'pageTree' => $pageTree]);
84 } 83 }
85 84
......
1 +<?php
2 +
3 +namespace Oxbow\Http\Controllers;
4 +
5 +use Illuminate\Http\Request;
6 +
7 +use Oxbow\Http\Requests;
8 +use Oxbow\Http\Controllers\Controller;
9 +use Oxbow\Repos\BookRepo;
10 +use Oxbow\Repos\ChapterRepo;
11 +
12 +class ChapterController extends Controller
13 +{
14 +
15 + protected $bookRepo;
16 + protected $chapterRepo;
17 +
18 + /**
19 + * ChapterController constructor.
20 + * @param $bookRepo
21 + * @param $chapterRepo
22 + */
23 + public function __construct(BookRepo $bookRepo,ChapterRepo $chapterRepo)
24 + {
25 + $this->bookRepo = $bookRepo;
26 + $this->chapterRepo = $chapterRepo;
27 + }
28 +
29 +
30 + /**
31 + * Display a listing of the resource.
32 + *
33 + * @return Response
34 + */
35 + public function index()
36 + {
37 + //
38 + }
39 +
40 + /**
41 + * Show the form for creating a new resource.
42 + *
43 + * @param $bookSlug
44 + * @return Response
45 + */
46 + public function create($bookSlug)
47 + {
48 + $book = $this->bookRepo->getBySlug($bookSlug);
49 + return view('chapters/create', ['book' => $book]);
50 + }
51 +
52 + /**
53 + * Store a newly created resource in storage.
54 + *
55 + * @param $bookSlug
56 + * @param Request $request
57 + * @return Response
58 + */
59 + public function store($bookSlug, Request $request)
60 + {
61 + $this->validate($request, [
62 + 'name' => 'required|string|max:255'
63 + ]);
64 +
65 + $book = $this->bookRepo->getBySlug($bookSlug);
66 + $chapter = $this->chapterRepo->newFromInput($request->all());
67 + $chapter->slug = $this->chapterRepo->findSuitableSlug($chapter->name, $book->id);
68 + $book->chapters()->save($chapter);
69 + return redirect($book->getUrl());
70 + }
71 +
72 + /**
73 + * Display the specified resource.
74 + *
75 + * @param int $id
76 + * @return Response
77 + */
78 + public function show($id)
79 + {
80 + //
81 + }
82 +
83 + /**
84 + * Show the form for editing the specified resource.
85 + *
86 + * @param int $id
87 + * @return Response
88 + */
89 + public function edit($id)
90 + {
91 + //
92 + }
93 +
94 + /**
95 + * Update the specified resource in storage.
96 + *
97 + * @param Request $request
98 + * @param int $id
99 + * @return Response
100 + */
101 + public function update(Request $request, $id)
102 + {
103 + //
104 + }
105 +
106 + /**
107 + * Remove the specified resource from storage.
108 + *
109 + * @param int $id
110 + * @return Response
111 + */
112 + public function destroy($id)
113 + {
114 + //
115 + }
116 +}
...@@ -26,10 +26,14 @@ Route::group(['prefix' => 'books'], function() { ...@@ -26,10 +26,14 @@ Route::group(['prefix' => 'books'], function() {
26 Route::post('/{bookSlug}/page', 'PageController@store'); 26 Route::post('/{bookSlug}/page', 'PageController@store');
27 Route::get('/{bookSlug}/sort', 'PageController@sortPages'); 27 Route::get('/{bookSlug}/sort', 'PageController@sortPages');
28 Route::put('/{bookSlug}/sort', 'PageController@savePageSort'); 28 Route::put('/{bookSlug}/sort', 'PageController@savePageSort');
29 - Route::get('/{bookSlug}/{pageSlug}', 'PageController@show'); 29 + Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
30 - Route::get('/{bookSlug}/{pageSlug}/create', 'PageController@create'); 30 + Route::get('/{bookSlug}/page/{pageSlug}/create', 'PageController@create');
31 - Route::get('/{bookSlug}/{pageSlug}/edit', 'PageController@edit'); 31 + Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
32 - Route::put('/{bookSlug}/{pageSlug}', 'PageController@update'); 32 + Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
33 +
34 + Route::get('/{bookSlug}/chapter/create', 'ChapterController@create');
35 + Route::post('/{bookSlug}/chapter/create', 'ChapterController@store');
36 +
33 }); 37 });
34 38
35 Route::post('/upload/image', 'ImageController@upload'); 39 Route::post('/upload/image', 'ImageController@upload');
......
...@@ -34,7 +34,7 @@ class Page extends Model ...@@ -34,7 +34,7 @@ class Page extends Model
34 34
35 public function getUrl() 35 public function getUrl()
36 { 36 {
37 - return '/books/' . $this->book->slug . '/' . $this->slug; 37 + return '/books/' . $this->book->slug . '/page/' . $this->slug;
38 } 38 }
39 39
40 } 40 }
......
1 +<?php namespace Oxbow\Repos;
2 +
3 +
4 +use Illuminate\Support\Str;
5 +use Oxbow\Chapter;
6 +
7 +class ChapterRepo
8 +{
9 +
10 + protected $chapter;
11 +
12 + /**
13 + * ChapterRepo constructor.
14 + * @param $chapter
15 + */
16 + public function __construct(Chapter $chapter)
17 + {
18 + $this->chapter = $chapter;
19 + }
20 +
21 + public function getById($id)
22 + {
23 + return $this->chapter->findOrFail($id);
24 + }
25 +
26 + public function getAll()
27 + {
28 + return $this->chapter->all();
29 + }
30 +
31 + public function getBySlug($slug, $bookId)
32 + {
33 + return $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId)->first();
34 + }
35 +
36 + public function newFromInput($input)
37 + {
38 + return $this->chapter->fill($input);
39 + }
40 +
41 + public function destroyById($id)
42 + {
43 + $page = $this->getById($id);
44 + $page->delete();
45 + }
46 +
47 + public function doesSlugExist($slug, $bookId, $currentId = false)
48 + {
49 + $query = $this->chapter->where('slug', '=', $slug)->where('book_id', '=', $bookId);
50 + if($currentId) {
51 + $query = $query->where('id', '!=', $currentId);
52 + }
53 + return $query->count() > 0;
54 + }
55 +
56 + public function findSuitableSlug($name, $bookId)
57 + {
58 + $slug = Str::slug($name);
59 + while($this->doesSlugExist($slug, $bookId)) {
60 + $slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
61 + }
62 + return $slug;
63 + }
64 +
65 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -114,7 +114,7 @@ class PageRepo ...@@ -114,7 +114,7 @@ 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)->orderBy('priority')->get(); 117 + return $this->page->where('book_id', '=', $bookId)->where('chapter_id', '=', 0)->orderBy('priority')->get();
118 } 118 }
119 119
120 /** 120 /**
......
...@@ -15,7 +15,7 @@ class CreatePagesTable extends Migration ...@@ -15,7 +15,7 @@ class CreatePagesTable extends Migration
15 Schema::create('pages', function (Blueprint $table) { 15 Schema::create('pages', function (Blueprint $table) {
16 $table->increments('id'); 16 $table->increments('id');
17 $table->integer('book_id'); 17 $table->integer('book_id');
18 - $table->integer('page_id'); 18 + $table->integer('chapter_id');
19 $table->string('name'); 19 $table->string('name');
20 $table->string('slug')->indexed(); 20 $table->string('slug')->indexed();
21 $table->longText('html'); 21 $table->longText('html');
......
1 +<?php
2 +
3 +use Illuminate\Database\Schema\Blueprint;
4 +use Illuminate\Database\Migrations\Migration;
5 +
6 +class CreateChaptersTable extends Migration
7 +{
8 + /**
9 + * Run the migrations.
10 + *
11 + * @return void
12 + */
13 + public function up()
14 + {
15 + Schema::create('chapters', function (Blueprint $table) {
16 + $table->increments('id');
17 + $table->integer('book_id');
18 + $table->string('slug')->indexed();
19 + $table->text('name');
20 + $table->text('description');
21 + $table->integer('priority');
22 + $table->timestamps();
23 + });
24 + }
25 +
26 + /**
27 + * Reverse the migrations.
28 + *
29 + * @return void
30 + */
31 + public function down()
32 + {
33 + Schema::drop('chapters');
34 + }
35 +}
1 var elixir = require('laravel-elixir'); 1 var elixir = require('laravel-elixir');
2 -require('laravel-elixir-livereload'); 2 +//require('laravel-elixir-livereload');
3 3
4 /* 4 /*
5 |-------------------------------------------------------------------------- 5 |--------------------------------------------------------------------------
...@@ -13,5 +13,5 @@ require('laravel-elixir-livereload'); ...@@ -13,5 +13,5 @@ require('laravel-elixir-livereload');
13 */ 13 */
14 14
15 elixir(function(mix) { 15 elixir(function(mix) {
16 - mix.sass('styles.scss').livereload(); 16 + mix.sass('styles.scss');//.livereload();
17 }); 17 });
......
1 -## Laravel PHP Framework 1 +# BookStack
2 2
3 -[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework) 3 +A platform to create documentation/wiki content.
4 -[![Total Downloads](https://poser.pugx.org/laravel/framework/d/total.svg)](https://packagist.org/packages/laravel/framework)
5 -[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework)
6 -[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework)
7 -[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework)
8 -
9 -Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching.
10 -
11 -Laravel is accessible, yet powerful, providing powerful tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked.
12 -
13 -## Official Documentation
14 -
15 -Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs).
16 -
17 -## Contributing
18 -
19 -Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions).
20 -
21 -## Security Vulnerabilities
22 -
23 -If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
24 -
25 -### License
26 -
27 -The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -379,4 +379,9 @@ body.dragging, body.dragging * { ...@@ -379,4 +379,9 @@ body.dragging, body.dragging * {
379 } 379 }
380 .sortable-page-list li.placeholder:before { 380 .sortable-page-list li.placeholder:before {
381 position: absolute; 381 position: absolute;
382 +}
383 +
384 +.material-icons {
385 + font-size: 1em;
386 + line-height: 1.4em;
382 } 387 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
4 <title>BookStack</title> 4 <title>BookStack</title>
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='http://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 <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>
......
...@@ -17,11 +17,27 @@ ...@@ -17,11 +17,27 @@
17 <p class="text-muted">{{$book->description}}</p> 17 <p class="text-muted">{{$book->description}}</p>
18 18
19 <div class="clearfix header-group"> 19 <div class="clearfix header-group">
20 - <h4 class="float">Pages</h4> 20 + <h4 class="float">Contents</h4>
21 - <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos float right">+ New Page</a> 21 + <div class="float right">
22 + <a href="{{$book->getUrl() . '/page/create'}}" class="text-pos">+ New Page</a>
23 + <a href="{{$book->getUrl() . '/chapter/create'}}" class="text-pos">+ New Chapter</a>
24 + </div>
25 + </div>
26 +
27 + <div class="page-list">
28 + @foreach($book->children() as $childElement)
29 + <div class="page-list-item">
30 + @if(is_a($childElement, 'Oxbow\Chapter'))
31 + <i class="fa fa-archive"></i>
32 + @else
33 + <i class="fa fa-file"></i>
34 + @endif
35 + {{$childElement->name}}
36 + </div>
37 + @endforeach
22 </div> 38 </div>
23 39
24 - @include('pages/page-tree-list', ['pageTree' => $pageTree]) 40 + {{--@include('pages/page-tree-list', ['pageTree' => $pageTree])--}}
25 41
26 </div> 42 </div>
27 43
......
1 +@extends('base')
2 +
3 +@section('content')
4 +
5 + <div class="page-content">
6 + <h1>Create New Chapter</h1>
7 + <form action="{{$book->getUrl()}}/chapter/create" method="POST">
8 + @include('chapters/form')
9 + </form>
10 + </div>
11 +
12 +@stop
...\ No newline at end of file ...\ No newline at end of file
1 +
2 +{{ csrf_field() }}
3 +<div class="form-group title-input">
4 + <label for="name">Chapter Name</label>
5 + @include('form/text', ['name' => 'name'])
6 +</div>
7 +<div class="form-group description-input">
8 + <label for="description">Description</label>
9 + @include('form/textarea', ['name' => 'description'])
10 +</div>
11 +<button type="submit" class="button pos">Save</button>
...\ No newline at end of file ...\ No newline at end of file