Dan Brown

Added page revision counting

Adds stored revision counts to pages and the revisions themselves.
Closes #321
...@@ -569,6 +569,7 @@ class EntityRepo ...@@ -569,6 +569,7 @@ class EntityRepo
569 $draftPage->html = $this->formatHtml($input['html']); 569 $draftPage->html = $this->formatHtml($input['html']);
570 $draftPage->text = strip_tags($draftPage->html); 570 $draftPage->text = strip_tags($draftPage->html);
571 $draftPage->draft = false; 571 $draftPage->draft = false;
572 + $draftPage->revision_count = 1;
572 573
573 $draftPage->save(); 574 $draftPage->save();
574 $this->savePageRevision($draftPage, trans('entities.pages_initial_revision')); 575 $this->savePageRevision($draftPage, trans('entities.pages_initial_revision'));
...@@ -593,6 +594,7 @@ class EntityRepo ...@@ -593,6 +594,7 @@ class EntityRepo
593 $revision->created_at = $page->updated_at; 594 $revision->created_at = $page->updated_at;
594 $revision->type = 'version'; 595 $revision->type = 'version';
595 $revision->summary = $summary; 596 $revision->summary = $summary;
597 + $revision->revision_number = $page->revision_count;
596 $revision->save(); 598 $revision->save();
597 599
598 // Clear old revisions 600 // Clear old revisions
...@@ -812,6 +814,7 @@ class EntityRepo ...@@ -812,6 +814,7 @@ class EntityRepo
812 $page->text = strip_tags($page->html); 814 $page->text = strip_tags($page->html);
813 if (setting('app-editor') !== 'markdown') $page->markdown = ''; 815 if (setting('app-editor') !== 'markdown') $page->markdown = '';
814 $page->updated_by = $userId; 816 $page->updated_by = $userId;
817 + $page->revision_count++;
815 $page->save(); 818 $page->save();
816 819
817 // Remove all update drafts for this user & page. 820 // Remove all update drafts for this user & page.
...@@ -920,6 +923,7 @@ class EntityRepo ...@@ -920,6 +923,7 @@ class EntityRepo
920 */ 923 */
921 public function restorePageRevision(Page $page, Book $book, $revisionId) 924 public function restorePageRevision(Page $page, Book $book, $revisionId)
922 { 925 {
926 + $page->revision_count++;
923 $this->savePageRevision($page); 927 $this->savePageRevision($page);
924 $revision = $page->revisions()->where('id', '=', $revisionId)->first(); 928 $revision = $page->revisions()->where('id', '=', $revisionId)->first();
925 $page->fill($revision->toArray()); 929 $page->fill($revision->toArray());
......
1 +<?php
2 +
3 +use Illuminate\Support\Facades\Schema;
4 +use Illuminate\Database\Schema\Blueprint;
5 +use Illuminate\Database\Migrations\Migration;
6 +
7 +class AddRevisionCounts extends Migration
8 +{
9 + /**
10 + * Run the migrations.
11 + *
12 + * @return void
13 + */
14 + public function up()
15 + {
16 + Schema::table('pages', function (Blueprint $table) {
17 + $table->integer('revision_count');
18 + });
19 + Schema::table('page_revisions', function (Blueprint $table) {
20 + $table->integer('revision_number');
21 + $table->index('revision_number');
22 + });
23 +
24 + // Update revision count
25 + $pTable = DB::getTablePrefix() . 'pages';
26 + $rTable = DB::getTablePrefix() . 'page_revisions';
27 + DB::statement("UPDATE ${pTable} SET ${pTable}.revision_count=(SELECT count(*) FROM ${rTable} WHERE ${rTable}.page_id=${pTable}.id)");
28 + }
29 +
30 + /**
31 + * Reverse the migrations.
32 + *
33 + * @return void
34 + */
35 + public function down()
36 + {
37 + Schema::table('pages', function (Blueprint $table) {
38 + $table->dropColumn('revision_count');
39 + });
40 + Schema::table('page_revisions', function (Blueprint $table) {
41 + $table->dropColumn('revision_number');
42 + });
43 + }
44 +}
...@@ -14,6 +14,7 @@ return [ ...@@ -14,6 +14,7 @@ return [
14 'recent_activity' => 'Recent Activity', 14 'recent_activity' => 'Recent Activity',
15 'create_now' => 'Create one now', 15 'create_now' => 'Create one now',
16 'revisions' => 'Revisions', 16 'revisions' => 'Revisions',
17 + 'meta_revision' => 'Revision #:revisionCount',
17 'meta_created' => 'Created :timeLength', 18 'meta_created' => 'Created :timeLength',
18 'meta_created_name' => 'Created :timeLength by :user', 19 'meta_created_name' => 'Created :timeLength by :user',
19 'meta_updated' => 'Updated :timeLength', 20 'meta_updated' => 'Updated :timeLength',
...@@ -168,6 +169,7 @@ return [ ...@@ -168,6 +169,7 @@ return [
168 'pages_revision_named' => 'Page Revision for :pageName', 169 'pages_revision_named' => 'Page Revision for :pageName',
169 'pages_revisions_created_by' => 'Created By', 170 'pages_revisions_created_by' => 'Created By',
170 'pages_revisions_date' => 'Revision Date', 171 'pages_revisions_date' => 'Revision Date',
172 + 'pages_revisions_number' => '#',
171 'pages_revisions_changelog' => 'Changelog', 173 'pages_revisions_changelog' => 'Changelog',
172 'pages_revisions_changes' => 'Changes', 174 'pages_revisions_changes' => 'Changes',
173 'pages_revisions_current' => 'Current Version', 175 'pages_revisions_current' => 'Current Version',
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 19
20 <table class="table"> 20 <table class="table">
21 <tr> 21 <tr>
22 + <th width="3%">{{ trans('entities.pages_revisions_number') }}</th>
22 <th width="23%">{{ trans('entities.pages_name') }}</th> 23 <th width="23%">{{ trans('entities.pages_name') }}</th>
23 <th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th> 24 <th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th>
24 <th width="15%">{{ trans('entities.pages_revisions_date') }}</th> 25 <th width="15%">{{ trans('entities.pages_revisions_date') }}</th>
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
27 </tr> 28 </tr>
28 @foreach($page->revisions as $index => $revision) 29 @foreach($page->revisions as $index => $revision)
29 <tr> 30 <tr>
31 + <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
30 <td>{{ $revision->name }}</td> 32 <td>{{ $revision->name }}</td>
31 <td style="line-height: 0;"> 33 <td style="line-height: 0;">
32 @if($revision->createdBy) 34 @if($revision->createdBy)
......
1 <p class="text-muted small"> 1 <p class="text-muted small">
2 + @if ($entity->isA('page')) {{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br> @endif
2 @if ($entity->createdBy) 3 @if ($entity->createdBy)
3 - {!! trans('entities.meta_created_name', ['timeLength' => $entity->created_at->diffForHumans(), 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"]) !!} 4 + {!! trans('entities.meta_created_name', [
5 + 'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
6 + 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
7 + ]) !!}
4 @else 8 @else
5 - {{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }} 9 + <span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
6 @endif 10 @endif
7 <br> 11 <br>
8 @if ($entity->updatedBy) 12 @if ($entity->updatedBy)
9 - {!! trans('entities.meta_updated_name', ['timeLength' => $entity->updated_at->diffForHumans(), 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"]) !!} 13 + {!! trans('entities.meta_updated_name', [
14 + 'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
15 + 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
16 + ]) !!}
10 @else 17 @else
11 - {{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }} 18 + <span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
12 @endif 19 @endif
13 </p> 20 </p>
...\ No newline at end of file ...\ No newline at end of file
......
1 <?php namespace Tests; 1 <?php namespace Tests;
2 2
3 +use BookStack\Book;
4 +use BookStack\Chapter;
5 +use BookStack\Page;
6 +use BookStack\Repos\EntityRepo;
7 +use BookStack\Repos\UserRepo;
8 +
3 class EntityTest extends BrowserKitTest 9 class EntityTest extends BrowserKitTest
4 { 10 {
5 11
...@@ -18,7 +24,7 @@ class EntityTest extends BrowserKitTest ...@@ -18,7 +24,7 @@ class EntityTest extends BrowserKitTest
18 $this->bookDelete($book); 24 $this->bookDelete($book);
19 } 25 }
20 26
21 - public function bookDelete(\BookStack\Book $book) 27 + public function bookDelete(Book $book)
22 { 28 {
23 $this->asAdmin() 29 $this->asAdmin()
24 ->visit($book->getUrl()) 30 ->visit($book->getUrl())
...@@ -32,7 +38,7 @@ class EntityTest extends BrowserKitTest ...@@ -32,7 +38,7 @@ class EntityTest extends BrowserKitTest
32 ->notSeeInDatabase('books', ['id' => $book->id]); 38 ->notSeeInDatabase('books', ['id' => $book->id]);
33 } 39 }
34 40
35 - public function bookUpdate(\BookStack\Book $book) 41 + public function bookUpdate(Book $book)
36 { 42 {
37 $newName = $book->name . ' Updated'; 43 $newName = $book->name . ' Updated';
38 $this->asAdmin() 44 $this->asAdmin()
...@@ -46,12 +52,12 @@ class EntityTest extends BrowserKitTest ...@@ -46,12 +52,12 @@ class EntityTest extends BrowserKitTest
46 ->seePageIs($book->getUrl() . '-updated') 52 ->seePageIs($book->getUrl() . '-updated')
47 ->see($newName); 53 ->see($newName);
48 54
49 - return \BookStack\Book::find($book->id); 55 + return Book::find($book->id);
50 } 56 }
51 57
52 public function test_book_sort_page_shows() 58 public function test_book_sort_page_shows()
53 { 59 {
54 - $books = \BookStack\Book::all(); 60 + $books = Book::all();
55 $bookToSort = $books[0]; 61 $bookToSort = $books[0];
56 $this->asAdmin() 62 $this->asAdmin()
57 ->visit($bookToSort->getUrl()) 63 ->visit($bookToSort->getUrl())
...@@ -65,7 +71,7 @@ class EntityTest extends BrowserKitTest ...@@ -65,7 +71,7 @@ class EntityTest extends BrowserKitTest
65 71
66 public function test_book_sort_item_returns_book_content() 72 public function test_book_sort_item_returns_book_content()
67 { 73 {
68 - $books = \BookStack\Book::all(); 74 + $books = Book::all();
69 $bookToSort = $books[0]; 75 $bookToSort = $books[0];
70 $firstPage = $bookToSort->pages[0]; 76 $firstPage = $bookToSort->pages[0];
71 $firstChapter = $bookToSort->chapters[0]; 77 $firstChapter = $bookToSort->chapters[0];
...@@ -79,7 +85,7 @@ class EntityTest extends BrowserKitTest ...@@ -79,7 +85,7 @@ class EntityTest extends BrowserKitTest
79 85
80 public function pageCreation($chapter) 86 public function pageCreation($chapter)
81 { 87 {
82 - $page = factory(\BookStack\Page::class)->make([ 88 + $page = factory(Page::class)->make([
83 'name' => 'My First Page' 89 'name' => 'My First Page'
84 ]); 90 ]);
85 91
...@@ -88,7 +94,7 @@ class EntityTest extends BrowserKitTest ...@@ -88,7 +94,7 @@ class EntityTest extends BrowserKitTest
88 ->visit($chapter->getUrl()) 94 ->visit($chapter->getUrl())
89 ->click('New Page'); 95 ->click('New Page');
90 96
91 - $draftPage = \BookStack\Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first(); 97 + $draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
92 98
93 $this->seePageIs($draftPage->getUrl()) 99 $this->seePageIs($draftPage->getUrl())
94 // Fill out form 100 // Fill out form
...@@ -99,13 +105,13 @@ class EntityTest extends BrowserKitTest ...@@ -99,13 +105,13 @@ class EntityTest extends BrowserKitTest
99 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page') 105 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
100 ->see($page->name); 106 ->see($page->name);
101 107
102 - $page = \BookStack\Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first(); 108 + $page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
103 return $page; 109 return $page;
104 } 110 }
105 111
106 - public function chapterCreation(\BookStack\Book $book) 112 + public function chapterCreation(Book $book)
107 { 113 {
108 - $chapter = factory(\BookStack\Chapter::class)->make([ 114 + $chapter = factory(Chapter::class)->make([
109 'name' => 'My First Chapter' 115 'name' => 'My First Chapter'
110 ]); 116 ]);
111 117
...@@ -122,13 +128,13 @@ class EntityTest extends BrowserKitTest ...@@ -122,13 +128,13 @@ class EntityTest extends BrowserKitTest
122 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter') 128 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
123 ->see($chapter->name)->see($chapter->description); 129 ->see($chapter->name)->see($chapter->description);
124 130
125 - $chapter = \BookStack\Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first(); 131 + $chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
126 return $chapter; 132 return $chapter;
127 } 133 }
128 134
129 public function bookCreation() 135 public function bookCreation()
130 { 136 {
131 - $book = factory(\BookStack\Book::class)->make([ 137 + $book = factory(Book::class)->make([
132 'name' => 'My First Book' 138 'name' => 'My First Book'
133 ]); 139 ]);
134 $this->asAdmin() 140 $this->asAdmin()
...@@ -154,7 +160,7 @@ class EntityTest extends BrowserKitTest ...@@ -154,7 +160,7 @@ class EntityTest extends BrowserKitTest
154 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/'; 160 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
155 $this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n"); 161 $this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
156 162
157 - $book = \BookStack\Book::where('slug', '=', 'my-first-book')->first(); 163 + $book = Book::where('slug', '=', 'my-first-book')->first();
158 return $book; 164 return $book;
159 } 165 }
160 166
...@@ -165,8 +171,8 @@ class EntityTest extends BrowserKitTest ...@@ -165,8 +171,8 @@ class EntityTest extends BrowserKitTest
165 $updater = $this->getEditor(); 171 $updater = $this->getEditor();
166 $entities = $this->createEntityChainBelongingToUser($creator, $updater); 172 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
167 $this->actingAs($creator); 173 $this->actingAs($creator);
168 - app('BookStack\Repos\UserRepo')->destroy($creator); 174 + app(UserRepo::class)->destroy($creator);
169 - app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']); 175 + app(EntityRepo::class)->savePageRevision($entities['page']);
170 176
171 $this->checkEntitiesViewable($entities); 177 $this->checkEntitiesViewable($entities);
172 } 178 }
...@@ -178,8 +184,8 @@ class EntityTest extends BrowserKitTest ...@@ -178,8 +184,8 @@ class EntityTest extends BrowserKitTest
178 $updater = $this->getEditor(); 184 $updater = $this->getEditor();
179 $entities = $this->createEntityChainBelongingToUser($creator, $updater); 185 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
180 $this->actingAs($updater); 186 $this->actingAs($updater);
181 - app('BookStack\Repos\UserRepo')->destroy($updater); 187 + app(UserRepo::class)->destroy($updater);
182 - app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']); 188 + app(EntityRepo::class)->savePageRevision($entities['page']);
183 189
184 $this->checkEntitiesViewable($entities); 190 $this->checkEntitiesViewable($entities);
185 } 191 }
...@@ -216,7 +222,7 @@ class EntityTest extends BrowserKitTest ...@@ -216,7 +222,7 @@ class EntityTest extends BrowserKitTest
216 222
217 public function test_old_page_slugs_redirect_to_new_pages() 223 public function test_old_page_slugs_redirect_to_new_pages()
218 { 224 {
219 - $page = \BookStack\Page::first(); 225 + $page = Page::first();
220 $pageUrl = $page->getUrl(); 226 $pageUrl = $page->getUrl();
221 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page'; 227 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
222 // Need to save twice since revisions are not generated in seeder. 228 // Need to save twice since revisions are not generated in seeder.
...@@ -225,7 +231,7 @@ class EntityTest extends BrowserKitTest ...@@ -225,7 +231,7 @@ class EntityTest extends BrowserKitTest
225 ->type('super test', '#name') 231 ->type('super test', '#name')
226 ->press('Save Page'); 232 ->press('Save Page');
227 233
228 - $page = \BookStack\Page::first(); 234 + $page = Page::first();
229 $pageUrl = $page->getUrl(); 235 $pageUrl = $page->getUrl();
230 236
231 // Second Save 237 // Second Save
...@@ -242,7 +248,7 @@ class EntityTest extends BrowserKitTest ...@@ -242,7 +248,7 @@ class EntityTest extends BrowserKitTest
242 248
243 public function test_recently_updated_pages_on_home() 249 public function test_recently_updated_pages_on_home()
244 { 250 {
245 - $page = \BookStack\Page::orderBy('updated_at', 'asc')->first(); 251 + $page = Page::orderBy('updated_at', 'asc')->first();
246 $this->asAdmin()->visit('/') 252 $this->asAdmin()->visit('/')
247 ->dontSeeInElement('#recently-updated-pages', $page->name); 253 ->dontSeeInElement('#recently-updated-pages', $page->name);
248 $this->visit($page->getUrl() . '/edit') 254 $this->visit($page->getUrl() . '/edit')
......
1 +<?php namespace Entity;
2 +
3 +
4 +use BookStack\Page;
5 +use Tests\TestCase;
6 +
7 +class PageRevisionTest extends TestCase
8 +{
9 +
10 + public function test_page_revision_count_increments_on_update()
11 + {
12 + $page = Page::first();
13 + $startCount = $page->revision_count;
14 +
15 + $resp = $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
16 + $resp->assertStatus(302);
17 +
18 + $this->assertTrue(Page::find($page->id)->revision_count === $startCount+1);
19 + }
20 +
21 + public function test_revision_count_shown_in_page_meta()
22 + {
23 + $page = Page::first();
24 + $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
25 + $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
26 + $page = Page::find($page->id);
27 +
28 + $pageView = $this->get($page->getUrl());
29 + $pageView->assertSee('Revision #' . $page->revision_count);
30 + }
31 +
32 +}
...\ No newline at end of file ...\ No newline at end of file