Dan Brown

Added page revisions. Fixes #2

...@@ -31,19 +31,8 @@ class PageController extends Controller ...@@ -31,19 +31,8 @@ class PageController extends Controller
31 $this->chapterRepo = $chapterRepo; 31 $this->chapterRepo = $chapterRepo;
32 } 32 }
33 33
34 -
35 /** 34 /**
36 - * Display a listing of the resource. 35 + * Show the form for creating a new page.
37 - *
38 - * @return Response
39 - */
40 - public function index()
41 - {
42 - //
43 - }
44 -
45 - /**
46 - * Show the form for creating a new resource.
47 * 36 *
48 * @param $bookSlug 37 * @param $bookSlug
49 * @param bool $chapterSlug 38 * @param bool $chapterSlug
...@@ -58,7 +47,7 @@ class PageController extends Controller ...@@ -58,7 +47,7 @@ class PageController extends Controller
58 } 47 }
59 48
60 /** 49 /**
61 - * Store a newly created resource in storage. 50 + * Store a newly created page in storage.
62 * 51 *
63 * @param Request $request 52 * @param Request $request
64 * @param $bookSlug 53 * @param $bookSlug
...@@ -86,11 +75,12 @@ class PageController extends Controller ...@@ -86,11 +75,12 @@ class PageController extends Controller
86 $page->created_by = Auth::user()->id; 75 $page->created_by = Auth::user()->id;
87 $page->updated_by = Auth::user()->id; 76 $page->updated_by = Auth::user()->id;
88 $page->save(); 77 $page->save();
78 + $this->pageRepo->saveRevision($page);
89 return redirect($page->getUrl()); 79 return redirect($page->getUrl());
90 } 80 }
91 81
92 /** 82 /**
93 - * Display the specified resource. 83 + * Display the specified page.
94 * 84 *
95 * @param $bookSlug 85 * @param $bookSlug
96 * @param $pageSlug 86 * @param $pageSlug
...@@ -100,12 +90,11 @@ class PageController extends Controller ...@@ -100,12 +90,11 @@ class PageController extends Controller
100 { 90 {
101 $book = $this->bookRepo->getBySlug($bookSlug); 91 $book = $this->bookRepo->getBySlug($bookSlug);
102 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 92 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
103 - //dd($sidebarBookTree);
104 return view('pages/show', ['page' => $page, 'book' => $book]); 93 return view('pages/show', ['page' => $page, 'book' => $book]);
105 } 94 }
106 95
107 /** 96 /**
108 - * Show the form for editing the specified resource. 97 + * Show the form for editing the specified page.
109 * 98 *
110 * @param $bookSlug 99 * @param $bookSlug
111 * @param $pageSlug 100 * @param $pageSlug
...@@ -119,7 +108,7 @@ class PageController extends Controller ...@@ -119,7 +108,7 @@ class PageController extends Controller
119 } 108 }
120 109
121 /** 110 /**
122 - * Update the specified resource in storage. 111 + * Update the specified page in storage.
123 * 112 *
124 * @param Request $request 113 * @param Request $request
125 * @param $bookSlug 114 * @param $bookSlug
...@@ -130,11 +119,7 @@ class PageController extends Controller ...@@ -130,11 +119,7 @@ class PageController extends Controller
130 { 119 {
131 $book = $this->bookRepo->getBySlug($bookSlug); 120 $book = $this->bookRepo->getBySlug($bookSlug);
132 $page = $this->pageRepo->getBySlug($pageSlug, $book->id); 121 $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
133 - $page->fill($request->all()); 122 + $this->pageRepo->updatePage($page, $book->id, $request->all());
134 - $page->slug = $this->pageRepo->findSuitableSlug($page->name, $book->id, $page->id);
135 - $page->text = strip_tags($page->html);
136 - $page->updated_by = Auth::user()->id;
137 - $page->save();
138 return redirect($page->getUrl()); 123 return redirect($page->getUrl());
139 } 124 }
140 125
...@@ -205,6 +190,12 @@ class PageController extends Controller ...@@ -205,6 +190,12 @@ class PageController extends Controller
205 return redirect($book->getUrl()); 190 return redirect($book->getUrl());
206 } 191 }
207 192
193 + /**
194 + * Show the deletion page for the specified page.
195 + * @param $bookSlug
196 + * @param $pageSlug
197 + * @return \Illuminate\View\View
198 + */
208 public function showDelete($bookSlug, $pageSlug) 199 public function showDelete($bookSlug, $pageSlug)
209 { 200 {
210 $book = $this->bookRepo->getBySlug($bookSlug); 201 $book = $this->bookRepo->getBySlug($bookSlug);
...@@ -213,7 +204,7 @@ class PageController extends Controller ...@@ -213,7 +204,7 @@ class PageController extends Controller
213 } 204 }
214 205
215 /** 206 /**
216 - * Remove the specified resource from storage. 207 + * Remove the specified page from storage.
217 * 208 *
218 * @param $bookSlug 209 * @param $bookSlug
219 * @param $pageSlug 210 * @param $pageSlug
...@@ -227,4 +218,42 @@ class PageController extends Controller ...@@ -227,4 +218,42 @@ class PageController extends Controller
227 $page->delete(); 218 $page->delete();
228 return redirect($book->getUrl()); 219 return redirect($book->getUrl());
229 } 220 }
221 +
222 + /**
223 + * Shows the last revisions for this page.
224 + * @param $bookSlug
225 + * @param $pageSlug
226 + * @return \Illuminate\View\View
227 + */
228 + public function showRevisions($bookSlug, $pageSlug)
229 + {
230 + $book = $this->bookRepo->getBySlug($bookSlug);
231 + $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
232 + return view('pages/revisions', ['page' => $page, 'book' => $book]);
233 + }
234 +
235 + /**
236 + * Shows a preview of a single revision
237 + * @param $bookSlug
238 + * @param $pageSlug
239 + * @param $revisionId
240 + * @return \Illuminate\View\View
241 + */
242 + public function showRevision($bookSlug, $pageSlug, $revisionId)
243 + {
244 + $book = $this->bookRepo->getBySlug($bookSlug);
245 + $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
246 + $revision = $this->pageRepo->getRevisionById($revisionId);
247 + $page->fill($revision->toArray());
248 + return view('pages/revision', ['page' => $page, 'book' => $book]);
249 + }
250 +
251 + public function restoreRevision($bookSlug, $pageSlug, $revisionId)
252 + {
253 + $book = $this->bookRepo->getBySlug($bookSlug);
254 + $page = $this->pageRepo->getBySlug($pageSlug, $book->id);
255 + $revision = $this->pageRepo->getRevisionById($revisionId);
256 + $page = $this->pageRepo->updatePage($page, $book->id, $revision->toArray());
257 + return redirect($page->getUrl());
258 + }
230 } 259 }
......
...@@ -18,6 +18,7 @@ Route::group(['middleware' => 'auth'], function() { ...@@ -18,6 +18,7 @@ Route::group(['middleware' => 'auth'], function() {
18 18
19 Route::group(['prefix' => 'books'], function() { 19 Route::group(['prefix' => 'books'], function() {
20 20
21 + // Books
21 Route::get('/', 'BookController@index'); 22 Route::get('/', 'BookController@index');
22 Route::get('/create', 'BookController@create'); 23 Route::get('/create', 'BookController@create');
23 Route::post('/', 'BookController@store'); 24 Route::post('/', 'BookController@store');
...@@ -27,6 +28,7 @@ Route::group(['middleware' => 'auth'], function() { ...@@ -27,6 +28,7 @@ Route::group(['middleware' => 'auth'], function() {
27 Route::get('/{slug}', 'BookController@show'); 28 Route::get('/{slug}', 'BookController@show');
28 Route::get('/{slug}/delete', 'BookController@showDelete'); 29 Route::get('/{slug}/delete', 'BookController@showDelete');
29 30
31 + // Pages
30 Route::get('/{bookSlug}/page/create', 'PageController@create'); 32 Route::get('/{bookSlug}/page/create', 'PageController@create');
31 Route::post('/{bookSlug}/page', 'PageController@store'); 33 Route::post('/{bookSlug}/page', 'PageController@store');
32 Route::get('/{bookSlug}/sort', 'PageController@sortPages'); 34 Route::get('/{bookSlug}/sort', 'PageController@sortPages');
...@@ -36,7 +38,12 @@ Route::group(['middleware' => 'auth'], function() { ...@@ -36,7 +38,12 @@ Route::group(['middleware' => 'auth'], function() {
36 Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); 38 Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
37 Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update'); 39 Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
38 Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy'); 40 Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy');
41 + //Revisions
42 + Route::get('/{bookSlug}/page/{pageSlug}/revisions', 'PageController@showRevisions');
43 + Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}', 'PageController@showRevision');
44 + Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}/restore', 'PageController@restoreRevision');
39 45
46 + // Chapters
40 Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create'); 47 Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
41 Route::get('/{bookSlug}/chapter/create', 'ChapterController@create'); 48 Route::get('/{bookSlug}/chapter/create', 'ChapterController@create');
42 Route::post('/{bookSlug}/chapter/create', 'ChapterController@store'); 49 Route::post('/{bookSlug}/chapter/create', 'ChapterController@store');
......
...@@ -42,6 +42,11 @@ class Page extends Model ...@@ -42,6 +42,11 @@ class Page extends Model
42 return $this->belongsTo('Oxbow\User', 'updated_by'); 42 return $this->belongsTo('Oxbow\User', 'updated_by');
43 } 43 }
44 44
45 + public function revisions()
46 + {
47 + return $this->hasMany('Oxbow\PageRevision')->orderBy('created_at', 'desc');
48 + }
49 +
45 public function getUrl() 50 public function getUrl()
46 { 51 {
47 return '/books/' . $this->book->slug . '/page/' . $this->slug; 52 return '/books/' . $this->book->slug . '/page/' . $this->slug;
......
1 +<?php
2 +
3 +namespace Oxbow;
4 +
5 +use Illuminate\Database\Eloquent\Model;
6 +
7 +class PageRevision extends Model
8 +{
9 + protected $fillable = ['name', 'html', 'text'];
10 +
11 + public function createdBy()
12 + {
13 + return $this->belongsTo('Oxbow\User', 'created_by');
14 + }
15 +
16 + public function page()
17 + {
18 + return $this->belongsTo('Oxbow\Page');
19 + }
20 +
21 + public function getUrl()
22 + {
23 + return $this->page->getUrl() . '/revisions/' . $this->id;
24 + }
25 +
26 +}
1 <?php namespace Oxbow\Repos; 1 <?php namespace Oxbow\Repos;
2 2
3 3
4 +use Illuminate\Support\Facades\Auth;
4 use Illuminate\Support\Str; 5 use Illuminate\Support\Str;
5 use Oxbow\Page; 6 use Oxbow\Page;
7 +use Oxbow\PageRevision;
6 8
7 class PageRepo 9 class PageRepo
8 { 10 {
9 protected $page; 11 protected $page;
12 + protected $pageRevision;
10 13
11 /** 14 /**
12 * PageRepo constructor. 15 * PageRepo constructor.
13 - * @param $page 16 + * @param Page $page
17 + * @param PageRevision $pageRevision
14 */ 18 */
15 - public function __construct(Page $page) 19 + public function __construct(Page $page, PageRevision $pageRevision)
16 { 20 {
17 $this->page = $page; 21 $this->page = $page;
22 + $this->pageRevision = $pageRevision;
18 } 23 }
19 24
20 public function idExists($id) 25 public function idExists($id)
...@@ -65,6 +70,68 @@ class PageRepo ...@@ -65,6 +70,68 @@ class PageRepo
65 } 70 }
66 71
67 /** 72 /**
73 + * Updates a page with any fillable data and saves it into the database.
74 + * @param Page $page
75 + * @param $book_id
76 + * @param $data
77 + * @return Page
78 + */
79 + public function updatePage(Page $page, $book_id, $data)
80 + {
81 + $page->fill($data);
82 + $page->slug = $this->findSuitableSlug($page->name, $book_id, $page->id);
83 + $page->text = strip_tags($page->html);
84 + $page->updated_by = Auth::user()->id;
85 + $page->save();
86 + $this->saveRevision($page);
87 + return $page;
88 + }
89 +
90 + /**
91 + * Saves a page revision into the system.
92 + * @param Page $page
93 + * @return $this
94 + */
95 + public function saveRevision(Page $page)
96 + {
97 + $lastRevision = $this->getLastRevision($page);
98 + if($lastRevision && ($lastRevision->html === $page->html && $lastRevision->name === $page->name)) {
99 + return $page;
100 + }
101 + $revision = $this->pageRevision->fill($page->toArray());
102 + $revision->page_id = $page->id;
103 + $revision->created_by = Auth::user()->id;
104 + $revision->save();
105 + // Clear old revisions
106 + if($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) {
107 + $this->pageRevision->where('page_id', '=', $page->id)
108 + ->orderBy('created_at', 'desc')->skip(50)->take(5)->delete();
109 + }
110 + return $revision;
111 + }
112 +
113 + /**
114 + * Gets the most recent revision for a page.
115 + * @param Page $page
116 + * @return mixed
117 + */
118 + public function getLastRevision(Page $page)
119 + {
120 + return $this->pageRevision->where('page_id', '=', $page->id)
121 + ->orderBy('created_at', 'desc')->first();
122 + }
123 +
124 + /**
125 + * Gets a single revision via it's id.
126 + * @param $id
127 + * @return mixed
128 + */
129 + public function getRevisionById($id)
130 + {
131 + return $this->pageRevision->findOrFail($id);
132 + }
133 +
134 + /**
68 * Checks if a slug exists within a book already. 135 * Checks if a slug exists within a book already.
69 * @param $slug 136 * @param $slug
70 * @param $bookId 137 * @param $bookId
......
1 +<?php
2 +
3 +use Illuminate\Database\Schema\Blueprint;
4 +use Illuminate\Database\Migrations\Migration;
5 +
6 +class CreatePageRevisionsTable extends Migration
7 +{
8 + /**
9 + * Run the migrations.
10 + *
11 + * @return void
12 + */
13 + public function up()
14 + {
15 + Schema::create('page_revisions', function (Blueprint $table) {
16 + $table->increments('id');
17 + $table->integer('page_id')->indexed();
18 + $table->string('name');
19 + $table->longText('html');
20 + $table->longText('text');
21 + $table->integer('created_by');
22 + $table->timestamps();
23 + });
24 + }
25 +
26 + /**
27 + * Reverse the migrations.
28 + *
29 + * @return void
30 + */
31 + public function down()
32 + {
33 + Schema::drop('page_revisions');
34 + }
35 +}
1 +<h1>{{$page->name}}</h1>
2 +@if(count($page->children) > 0)
3 + <h4 class="text-muted">Sub-pages</h4>
4 + <div class="page-list">
5 + @foreach($page->children as $childPage)
6 + <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a>
7 + @endforeach
8 + </div>
9 +@endif
10 +{!! $page->html !!}
...\ No newline at end of file ...\ No newline at end of file
1 +@extends('base')
2 +
3 +@section('content')
4 +
5 + <div class="page-content">
6 + @include('pages/page-display')
7 + </div>
8 +
9 +@stop
1 +@extends('base')
2 +
3 +@section('content')
4 +
5 + <div class="row faded-small">
6 + <div class="col-md-6 faded">
7 + <div class="breadcrumbs padded-horizontal">
8 + <a href="{{$page->getUrl()}}" class="text-primary"><i class="zmdi zmdi-arrow-left"></i>Back to page</a>
9 + </div>
10 + </div>
11 + <div class="col-md-6 faded">
12 + </div>
13 + </div>
14 +
15 + <div class="page-content">
16 + <h1>Page Revisions <span class="subheader">For "{{ $page->name }}"</span></h1>
17 +
18 + @if(count($page->revisions) > 0)
19 +
20 + <table class="table">
21 + <tr>
22 + <th>Name</th>
23 + <th>Created By</th>
24 + <th>Revision Date</th>
25 + <th>Actions</th>
26 + </tr>
27 + @foreach($page->revisions as $revision)
28 + <tr>
29 + <td>{{$revision->name}}</td>
30 + <td>{{$revision->createdBy->name}}</td>
31 + <td><small>{{$revision->created_at->format('jS F, Y H:i:s')}} ({{$revision->created_at->diffForHumans()}})</small></td>
32 + <td>
33 + <a href="{{$revision->getUrl()}}" target="_blank">Preview</a>
34 + <span class="text-muted">&nbsp;|&nbsp;</span>
35 + <a href="{{$revision->getUrl()}}/restore">Restore</a>
36 + </td>
37 + </tr>
38 + @endforeach
39 + </table>
40 +
41 + @else
42 + <p>This page has no revisions.</p>
43 + @endif
44 +
45 + </div>
46 +
47 +@stop
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
17 </div> 17 </div>
18 <div class="col-md-6 faded"> 18 <div class="col-md-6 faded">
19 <div class="action-buttons"> 19 <div class="action-buttons">
20 + <a href="{{$page->getUrl() . '/revisions'}}" class="text-primary"><i class="zmdi zmdi-replay"></i>Revisions</a>
20 <a href="{{$page->getUrl() . '/edit'}}" class="text-primary" ><i class="zmdi zmdi-edit"></i>Edit</a> 21 <a href="{{$page->getUrl() . '/edit'}}" class="text-primary" ><i class="zmdi zmdi-edit"></i>Edit</a>
21 <a href="{{$page->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a> 22 <a href="{{$page->getUrl() . '/delete'}}" class="text-neg"><i class="zmdi zmdi-delete"></i>Delete</a>
22 </div> 23 </div>
...@@ -31,17 +32,7 @@ ...@@ -31,17 +32,7 @@
31 32
32 33
33 <div class="page-content"> 34 <div class="page-content">
34 - <h1>{{$page->name}}</h1> 35 + @include('pages/page-display')
35 - @if(count($page->children) > 0)
36 - <h4 class="text-muted">Sub-pages</h4>
37 - <div class="page-list">
38 - @foreach($page->children as $childPage)
39 - <a href="{{ $childPage->getUrl() }}">{{ $childPage->name }}</a>
40 - @endforeach
41 - </div>
42 - @endif
43 - {!! $page->html !!}
44 -
45 <hr> 36 <hr>
46 <p class="text-muted small"> 37 <p class="text-muted small">
47 Created {{$page->created_at->diffForHumans()}} @if($page->createdBy) by {{$page->createdBy->name}} @endif 38 Created {{$page->created_at->diffForHumans()}} @if($page->createdBy) by {{$page->createdBy->name}} @endif
......