mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-08-26 20:22:18 +00:00
Added page revision counting
Adds stored revision counts to pages and the revisions themselves. Closes #321
This commit is contained in:
parent
87e18b8068
commit
4c985aac7e
@ -569,6 +569,7 @@ class EntityRepo
|
|||||||
$draftPage->html = $this->formatHtml($input['html']);
|
$draftPage->html = $this->formatHtml($input['html']);
|
||||||
$draftPage->text = strip_tags($draftPage->html);
|
$draftPage->text = strip_tags($draftPage->html);
|
||||||
$draftPage->draft = false;
|
$draftPage->draft = false;
|
||||||
|
$draftPage->revision_count = 1;
|
||||||
|
|
||||||
$draftPage->save();
|
$draftPage->save();
|
||||||
$this->savePageRevision($draftPage, trans('entities.pages_initial_revision'));
|
$this->savePageRevision($draftPage, trans('entities.pages_initial_revision'));
|
||||||
@ -593,6 +594,7 @@ class EntityRepo
|
|||||||
$revision->created_at = $page->updated_at;
|
$revision->created_at = $page->updated_at;
|
||||||
$revision->type = 'version';
|
$revision->type = 'version';
|
||||||
$revision->summary = $summary;
|
$revision->summary = $summary;
|
||||||
|
$revision->revision_number = $page->revision_count;
|
||||||
$revision->save();
|
$revision->save();
|
||||||
|
|
||||||
// Clear old revisions
|
// Clear old revisions
|
||||||
@ -812,6 +814,7 @@ class EntityRepo
|
|||||||
$page->text = strip_tags($page->html);
|
$page->text = strip_tags($page->html);
|
||||||
if (setting('app-editor') !== 'markdown') $page->markdown = '';
|
if (setting('app-editor') !== 'markdown') $page->markdown = '';
|
||||||
$page->updated_by = $userId;
|
$page->updated_by = $userId;
|
||||||
|
$page->revision_count++;
|
||||||
$page->save();
|
$page->save();
|
||||||
|
|
||||||
// Remove all update drafts for this user & page.
|
// Remove all update drafts for this user & page.
|
||||||
@ -920,6 +923,7 @@ class EntityRepo
|
|||||||
*/
|
*/
|
||||||
public function restorePageRevision(Page $page, Book $book, $revisionId)
|
public function restorePageRevision(Page $page, Book $book, $revisionId)
|
||||||
{
|
{
|
||||||
|
$page->revision_count++;
|
||||||
$this->savePageRevision($page);
|
$this->savePageRevision($page);
|
||||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||||
$page->fill($revision->toArray());
|
$page->fill($revision->toArray());
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddRevisionCounts extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('pages', function (Blueprint $table) {
|
||||||
|
$table->integer('revision_count');
|
||||||
|
});
|
||||||
|
Schema::table('page_revisions', function (Blueprint $table) {
|
||||||
|
$table->integer('revision_number');
|
||||||
|
$table->index('revision_number');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update revision count
|
||||||
|
$pTable = DB::getTablePrefix() . 'pages';
|
||||||
|
$rTable = DB::getTablePrefix() . 'page_revisions';
|
||||||
|
DB::statement("UPDATE ${pTable} SET ${pTable}.revision_count=(SELECT count(*) FROM ${rTable} WHERE ${rTable}.page_id=${pTable}.id)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('pages', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('revision_count');
|
||||||
|
});
|
||||||
|
Schema::table('page_revisions', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('revision_number');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ return [
|
|||||||
'recent_activity' => 'Recent Activity',
|
'recent_activity' => 'Recent Activity',
|
||||||
'create_now' => 'Create one now',
|
'create_now' => 'Create one now',
|
||||||
'revisions' => 'Revisions',
|
'revisions' => 'Revisions',
|
||||||
|
'meta_revision' => 'Revision #:revisionCount',
|
||||||
'meta_created' => 'Created :timeLength',
|
'meta_created' => 'Created :timeLength',
|
||||||
'meta_created_name' => 'Created :timeLength by :user',
|
'meta_created_name' => 'Created :timeLength by :user',
|
||||||
'meta_updated' => 'Updated :timeLength',
|
'meta_updated' => 'Updated :timeLength',
|
||||||
@ -168,6 +169,7 @@ return [
|
|||||||
'pages_revision_named' => 'Page Revision for :pageName',
|
'pages_revision_named' => 'Page Revision for :pageName',
|
||||||
'pages_revisions_created_by' => 'Created By',
|
'pages_revisions_created_by' => 'Created By',
|
||||||
'pages_revisions_date' => 'Revision Date',
|
'pages_revisions_date' => 'Revision Date',
|
||||||
|
'pages_revisions_number' => '#',
|
||||||
'pages_revisions_changelog' => 'Changelog',
|
'pages_revisions_changelog' => 'Changelog',
|
||||||
'pages_revisions_changes' => 'Changes',
|
'pages_revisions_changes' => 'Changes',
|
||||||
'pages_revisions_current' => 'Current Version',
|
'pages_revisions_current' => 'Current Version',
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
|
<th width="3%">{{ trans('entities.pages_revisions_number') }}</th>
|
||||||
<th width="23%">{{ trans('entities.pages_name') }}</th>
|
<th width="23%">{{ trans('entities.pages_name') }}</th>
|
||||||
<th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th>
|
<th colspan="2" width="8%">{{ trans('entities.pages_revisions_created_by') }}</th>
|
||||||
<th width="15%">{{ trans('entities.pages_revisions_date') }}</th>
|
<th width="15%">{{ trans('entities.pages_revisions_date') }}</th>
|
||||||
@ -27,6 +28,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
@foreach($page->revisions as $index => $revision)
|
@foreach($page->revisions as $index => $revision)
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
|
||||||
<td>{{ $revision->name }}</td>
|
<td>{{ $revision->name }}</td>
|
||||||
<td style="line-height: 0;">
|
<td style="line-height: 0;">
|
||||||
@if($revision->createdBy)
|
@if($revision->createdBy)
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
<p class="text-muted small">
|
<p class="text-muted small">
|
||||||
|
@if ($entity->isA('page')) {{ trans('entities.meta_revision', ['revisionCount' => $entity->revision_count]) }} <br> @endif
|
||||||
@if ($entity->createdBy)
|
@if ($entity->createdBy)
|
||||||
{!! trans('entities.meta_created_name', ['timeLength' => $entity->created_at->diffForHumans(), 'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"]) !!}
|
{!! trans('entities.meta_created_name', [
|
||||||
|
'timeLength' => '<span title="'.$entity->created_at->toDayDateTimeString().'">'.$entity->created_at->diffForHumans() . '</span>',
|
||||||
|
'user' => "<a href='{$entity->createdBy->getProfileUrl()}'>".htmlentities($entity->createdBy->name). "</a>"
|
||||||
|
]) !!}
|
||||||
@else
|
@else
|
||||||
{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}
|
<span title="{{$entity->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }}</span>
|
||||||
@endif
|
@endif
|
||||||
<br>
|
<br>
|
||||||
@if ($entity->updatedBy)
|
@if ($entity->updatedBy)
|
||||||
{!! trans('entities.meta_updated_name', ['timeLength' => $entity->updated_at->diffForHumans(), 'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"]) !!}
|
{!! trans('entities.meta_updated_name', [
|
||||||
|
'timeLength' => '<span title="' . $entity->updated_at->toDayDateTimeString() .'">' . $entity->updated_at->diffForHumans() .'</span>',
|
||||||
|
'user' => "<a href='{$entity->updatedBy->getProfileUrl()}'>".htmlentities($entity->updatedBy->name). "</a>"
|
||||||
|
]) !!}
|
||||||
@else
|
@else
|
||||||
{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}
|
<span title="{{ $entity->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }}</span>
|
||||||
@endif
|
@endif
|
||||||
</p>
|
</p>
|
@ -1,5 +1,11 @@
|
|||||||
<?php namespace Tests;
|
<?php namespace Tests;
|
||||||
|
|
||||||
|
use BookStack\Book;
|
||||||
|
use BookStack\Chapter;
|
||||||
|
use BookStack\Page;
|
||||||
|
use BookStack\Repos\EntityRepo;
|
||||||
|
use BookStack\Repos\UserRepo;
|
||||||
|
|
||||||
class EntityTest extends BrowserKitTest
|
class EntityTest extends BrowserKitTest
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -18,7 +24,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
$this->bookDelete($book);
|
$this->bookDelete($book);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bookDelete(\BookStack\Book $book)
|
public function bookDelete(Book $book)
|
||||||
{
|
{
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit($book->getUrl())
|
->visit($book->getUrl())
|
||||||
@ -32,7 +38,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
->notSeeInDatabase('books', ['id' => $book->id]);
|
->notSeeInDatabase('books', ['id' => $book->id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bookUpdate(\BookStack\Book $book)
|
public function bookUpdate(Book $book)
|
||||||
{
|
{
|
||||||
$newName = $book->name . ' Updated';
|
$newName = $book->name . ' Updated';
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
@ -46,12 +52,12 @@ class EntityTest extends BrowserKitTest
|
|||||||
->seePageIs($book->getUrl() . '-updated')
|
->seePageIs($book->getUrl() . '-updated')
|
||||||
->see($newName);
|
->see($newName);
|
||||||
|
|
||||||
return \BookStack\Book::find($book->id);
|
return Book::find($book->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_book_sort_page_shows()
|
public function test_book_sort_page_shows()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all();
|
$books = Book::all();
|
||||||
$bookToSort = $books[0];
|
$bookToSort = $books[0];
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
->visit($bookToSort->getUrl())
|
->visit($bookToSort->getUrl())
|
||||||
@ -65,7 +71,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_book_sort_item_returns_book_content()
|
public function test_book_sort_item_returns_book_content()
|
||||||
{
|
{
|
||||||
$books = \BookStack\Book::all();
|
$books = Book::all();
|
||||||
$bookToSort = $books[0];
|
$bookToSort = $books[0];
|
||||||
$firstPage = $bookToSort->pages[0];
|
$firstPage = $bookToSort->pages[0];
|
||||||
$firstChapter = $bookToSort->chapters[0];
|
$firstChapter = $bookToSort->chapters[0];
|
||||||
@ -79,7 +85,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function pageCreation($chapter)
|
public function pageCreation($chapter)
|
||||||
{
|
{
|
||||||
$page = factory(\BookStack\Page::class)->make([
|
$page = factory(Page::class)->make([
|
||||||
'name' => 'My First Page'
|
'name' => 'My First Page'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -88,7 +94,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
->visit($chapter->getUrl())
|
->visit($chapter->getUrl())
|
||||||
->click('New Page');
|
->click('New Page');
|
||||||
|
|
||||||
$draftPage = \BookStack\Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
|
$draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
|
||||||
|
|
||||||
$this->seePageIs($draftPage->getUrl())
|
$this->seePageIs($draftPage->getUrl())
|
||||||
// Fill out form
|
// Fill out form
|
||||||
@ -99,13 +105,13 @@ class EntityTest extends BrowserKitTest
|
|||||||
->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
|
->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
|
||||||
->see($page->name);
|
->see($page->name);
|
||||||
|
|
||||||
$page = \BookStack\Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
|
$page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function chapterCreation(\BookStack\Book $book)
|
public function chapterCreation(Book $book)
|
||||||
{
|
{
|
||||||
$chapter = factory(\BookStack\Chapter::class)->make([
|
$chapter = factory(Chapter::class)->make([
|
||||||
'name' => 'My First Chapter'
|
'name' => 'My First Chapter'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -122,13 +128,13 @@ class EntityTest extends BrowserKitTest
|
|||||||
->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
|
->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
|
||||||
->see($chapter->name)->see($chapter->description);
|
->see($chapter->name)->see($chapter->description);
|
||||||
|
|
||||||
$chapter = \BookStack\Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
|
$chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
|
||||||
return $chapter;
|
return $chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bookCreation()
|
public function bookCreation()
|
||||||
{
|
{
|
||||||
$book = factory(\BookStack\Book::class)->make([
|
$book = factory(Book::class)->make([
|
||||||
'name' => 'My First Book'
|
'name' => 'My First Book'
|
||||||
]);
|
]);
|
||||||
$this->asAdmin()
|
$this->asAdmin()
|
||||||
@ -154,7 +160,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
$expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
|
$expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
|
||||||
$this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
|
$this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
|
||||||
|
|
||||||
$book = \BookStack\Book::where('slug', '=', 'my-first-book')->first();
|
$book = Book::where('slug', '=', 'my-first-book')->first();
|
||||||
return $book;
|
return $book;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,8 +171,8 @@ class EntityTest extends BrowserKitTest
|
|||||||
$updater = $this->getEditor();
|
$updater = $this->getEditor();
|
||||||
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
||||||
$this->actingAs($creator);
|
$this->actingAs($creator);
|
||||||
app('BookStack\Repos\UserRepo')->destroy($creator);
|
app(UserRepo::class)->destroy($creator);
|
||||||
app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']);
|
app(EntityRepo::class)->savePageRevision($entities['page']);
|
||||||
|
|
||||||
$this->checkEntitiesViewable($entities);
|
$this->checkEntitiesViewable($entities);
|
||||||
}
|
}
|
||||||
@ -178,8 +184,8 @@ class EntityTest extends BrowserKitTest
|
|||||||
$updater = $this->getEditor();
|
$updater = $this->getEditor();
|
||||||
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
||||||
$this->actingAs($updater);
|
$this->actingAs($updater);
|
||||||
app('BookStack\Repos\UserRepo')->destroy($updater);
|
app(UserRepo::class)->destroy($updater);
|
||||||
app('BookStack\Repos\EntityRepo')->savePageRevision($entities['page']);
|
app(EntityRepo::class)->savePageRevision($entities['page']);
|
||||||
|
|
||||||
$this->checkEntitiesViewable($entities);
|
$this->checkEntitiesViewable($entities);
|
||||||
}
|
}
|
||||||
@ -216,7 +222,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_old_page_slugs_redirect_to_new_pages()
|
public function test_old_page_slugs_redirect_to_new_pages()
|
||||||
{
|
{
|
||||||
$page = \BookStack\Page::first();
|
$page = Page::first();
|
||||||
$pageUrl = $page->getUrl();
|
$pageUrl = $page->getUrl();
|
||||||
$newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
|
$newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
|
||||||
// Need to save twice since revisions are not generated in seeder.
|
// Need to save twice since revisions are not generated in seeder.
|
||||||
@ -225,7 +231,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
->type('super test', '#name')
|
->type('super test', '#name')
|
||||||
->press('Save Page');
|
->press('Save Page');
|
||||||
|
|
||||||
$page = \BookStack\Page::first();
|
$page = Page::first();
|
||||||
$pageUrl = $page->getUrl();
|
$pageUrl = $page->getUrl();
|
||||||
|
|
||||||
// Second Save
|
// Second Save
|
||||||
@ -242,7 +248,7 @@ class EntityTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_recently_updated_pages_on_home()
|
public function test_recently_updated_pages_on_home()
|
||||||
{
|
{
|
||||||
$page = \BookStack\Page::orderBy('updated_at', 'asc')->first();
|
$page = Page::orderBy('updated_at', 'asc')->first();
|
||||||
$this->asAdmin()->visit('/')
|
$this->asAdmin()->visit('/')
|
||||||
->dontSeeInElement('#recently-updated-pages', $page->name);
|
->dontSeeInElement('#recently-updated-pages', $page->name);
|
||||||
$this->visit($page->getUrl() . '/edit')
|
$this->visit($page->getUrl() . '/edit')
|
||||||
|
32
tests/Entity/PageRevisionTest.php
Normal file
32
tests/Entity/PageRevisionTest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php namespace Entity;
|
||||||
|
|
||||||
|
|
||||||
|
use BookStack\Page;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PageRevisionTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function test_page_revision_count_increments_on_update()
|
||||||
|
{
|
||||||
|
$page = Page::first();
|
||||||
|
$startCount = $page->revision_count;
|
||||||
|
|
||||||
|
$resp = $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
|
||||||
|
$resp->assertStatus(302);
|
||||||
|
|
||||||
|
$this->assertTrue(Page::find($page->id)->revision_count === $startCount+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_revision_count_shown_in_page_meta()
|
||||||
|
{
|
||||||
|
$page = Page::first();
|
||||||
|
$this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
|
||||||
|
$this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
|
||||||
|
$page = Page::find($page->id);
|
||||||
|
|
||||||
|
$pageView = $this->get($page->getUrl());
|
||||||
|
$pageView->assertSee('Revision #' . $page->revision_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user