]> BookStack Code Mirror - bookstack/commitdiff
Merge pull request #1008 from BookStackApp/revision-deletion
authorAbijeet Patro <redacted>
Sun, 16 Sep 2018 16:00:04 +0000 (21:30 +0530)
committerGitHub <redacted>
Sun, 16 Sep 2018 16:00:04 +0000 (21:30 +0530)
#784 - Adds ability to remove particular revision.

23 files changed:
app/Http/Controllers/PageController.php
app/Page.php
resources/assets/sass/_lists.scss
resources/assets/sass/_tables.scss
resources/lang/de/entities.php
resources/lang/en/entities.php
resources/lang/es/entities.php
resources/lang/es_AR/entities.php
resources/lang/fr/entities.php
resources/lang/it/entities.php
resources/lang/ja/entities.php
resources/lang/nl/entities.php
resources/lang/pl/entities.php
resources/lang/pt_BR/entities.php
resources/lang/ru/entities.php
resources/lang/sk/entities.php
resources/lang/sv/entities.php
resources/lang/zh_CN/entities.php
resources/lang/zh_TW/entities.php
resources/views/pages/revisions.blade.php
resources/views/partials/custom-styles.blade.php
routes/web.php
tests/Entity/PageRevisionTest.php

index 25a0503ebeab00245d69a85389ddd28b5b7db804..e3079047cac436c13873cdf8876841d98a23f165 100644 (file)
@@ -454,6 +454,40 @@ class PageController extends Controller
         return redirect($page->getUrl());
     }
 
+
+    /**
+     * Deletes a revision using the id of the specified revision.
+     * @param string $bookSlug
+     * @param string $pageSlug
+     * @param int $revId
+     * @throws NotFoundException
+     * @throws BadRequestException
+     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     */
+    public function destroyRevision($bookSlug, $pageSlug, $revId)
+    {
+        $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
+        $this->checkOwnablePermission('page-delete', $page);
+
+        $revision = $page->revisions()->where('id', '=', $revId)->first();
+        if ($revision === null) {
+            throw new NotFoundException("Revision #{$revId} not found");
+        }
+
+        // Get the current revision for the page
+        $currentRevision = $page->getCurrentRevision();
+
+        // Check if its the latest revision, cannot delete latest revision.
+        if (intval($currentRevision->id) === intval($revId)) {
+            session()->flash('error', trans('entities.revision_cannot_delete_latest'));
+            return response()->view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page], 400);
+        }
+
+        $revision->delete();
+        session()->flash('success', trans('entities.revision_delete_success'));
+        return view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]);
+    }
+
     /**
      * Exports a page to a PDF.
      * https://github.com/barryvdh/laravel-dompdf
index 9554504b35d2466e4c8741ce41bca05adaa8e966..5c03e7d66f610d21b8499370307314b924bcca3d 100644 (file)
@@ -112,4 +112,13 @@ class Page extends Entity
         $htmlQuery = $withContent ? 'html' : "'' as html";
         return "'BookStack\\\\Page' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, {$htmlQuery}, book_id, priority, chapter_id, draft, created_by, updated_by, updated_at, created_at";
     }
+
+    /**
+     * Get the current revision for the page if existing
+     * @return \BookStack\PageRevision|null
+     */
+    public function getCurrentRevision()
+    {
+        return $this->revisions()->first();
+    }
 }
index 3338b39383f4dd99ffbecc57bf910fe0aa039075..e8d131b52b9f1a954b5fd9dfac9882da3f9c39d8 100644 (file)
@@ -367,7 +367,7 @@ ul.pagination {
     padding: $-xs $-m;
     line-height: 1.2;
   }
-  a {
+  a, button {
     display: block;
     padding: $-xs $-m;
     color: #555;
@@ -382,6 +382,10 @@ ul.pagination {
       width: 16px;
     }
   }
+  button {
+    width: 100%;
+    text-align: left;
+  }
   li.border-bottom {
     border-bottom: 1px solid #DDD;
   }
index 38b044268b7c587311067eb718987d4d4fc24e60..ec24e2fa64f51be5be1cda6eab84eca656651244 100644 (file)
@@ -41,6 +41,9 @@ table.table {
   .text-center {
     text-align: center;
   }
+  td.actions {
+    overflow: visible;
+  }
 }
 
 table.no-style {
index 96aaa9b0ef761e887265220003257a5a721d7e7a..7c27be17b175c029aac83edc20d1c5818e2878c5 100644 (file)
@@ -256,4 +256,11 @@ return [
     'comment_updated_success' => 'Kommentar aktualisiert',
     'comment_delete_confirm' => 'Möchten Sie diesen Kommentar wirklich löschen?',
     'comment_in_reply_to' => 'Antwort auf :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Sind Sie sicher, dass Sie diese Revision löschen wollen?',
+    'revision_delete_success' => 'Revision gelöscht',
+    'revision_cannot_delete_latest' => 'Die letzte Version kann nicht gelöscht werden.'
 ];
index 93025ffd4e235be72fa11ca297ccab83feff1a5f..c998874011d4abc6fe60a9770ca1c16f474c940f 100644 (file)
@@ -265,4 +265,11 @@ return [
     'comment_updated_success' => 'Comment updated',
     'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
     'comment_in_reply_to' => 'In reply to :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_delete_success' => 'Revision deleted',
+    'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
 ];
\ No newline at end of file
index 8c5c9f07f46181ee807f8a06654d82bb5acc2ae2..a84d72fffe689bd0c44e7a24b5c0679e02c96a29 100644 (file)
@@ -265,4 +265,11 @@ return [
     'comment_updated_success' => 'Comentario actualizado',
     'comment_delete_confirm' => '¿Está seguro de que quiere borrar este comentario?',
     'comment_in_reply_to' => 'En respuesta a :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => '¿Está seguro de que desea eliminar esta revisión?',
+    'revision_delete_success' => 'Revisión eliminada',
+    'revision_cannot_delete_latest' => 'No se puede eliminar la última revisión.'
 ];
index 371f1b7aa458bab2b0e8a6888967667854c93c0b..91d156e53452cd018c2af0253561044031fc7b9d 100644 (file)
@@ -265,4 +265,11 @@ return [
     'comment_updated_success' => 'Comentario actualizado',
     'comment_delete_confirm' => '¿Está seguro que quiere borrar este comentario?',
     'comment_in_reply_to' => 'En respuesta a :commentId',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_delete_success' => 'Revisión eliminada',
+    'revision_cannot_delete_latest' => 'No se puede eliminar la última revisión.'
 ];
index b2009777520f2b437e920bd839ba3b9034377004..deee70ee44c745ecd12a64b3a7bf3f8815bb2d0c 100644 (file)
@@ -265,4 +265,11 @@ return [
     'comment_updated_success' => 'Commentaire mis à jour',
     'comment_delete_confirm' => 'Etes-vous sûr de vouloir supprimer ce commentaire ?',
     'comment_in_reply_to' => 'En réponse à :commentId',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Êtes-vous sûr de vouloir supprimer cette révision?',
+    'revision_delete_success' => 'Révision supprimée',
+    'revision_cannot_delete_latest' => 'Impossible de supprimer la dernière révision.'
 ];
\ No newline at end of file
index 1941ffb1e62a98193c707bb22ce1dc731e96063c..ad1733b9161f7262cb6a453880a6517586e49ff2 100755 (executable)
@@ -260,4 +260,11 @@ return [
     'comment_updated_success' => 'Commento aggiornato',
     'comment_delete_confirm' => 'Sei sicuro di voler elminare questo commento?',
     'comment_in_reply_to' => 'In risposta a :commentId',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Sei sicuro di voler eliminare questa revisione?',
+    'revision_delete_success' => 'Revisione cancellata',
+    'revision_cannot_delete_latest' => 'Impossibile eliminare l\'ultima revisione.'
 ];
\ No newline at end of file
index c08c4998bdd2c2412f410e5df326c309208d1fc0..f177154f442ff37d8bfed961dc09952c625fcb0f 100644 (file)
@@ -257,4 +257,11 @@ return [
     'comment_updated_success' => 'コメントを更新しました',
     'comment_delete_confirm' => '本当にこのコメントを削除しますか?',
     'comment_in_reply_to' => ':commentIdへ返信',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'このリビジョンを削除しますか?',
+    'revision_delete_success' => 'リビジョンを削除しました',
+    'revision_cannot_delete_latest' => '最新のリビジョンを削除できません。'
 ];
index a807c84cea93d1eddecd78c286958e28647f3d85..29bb11a37849e0dca529b56c5555ba3761fb01b8 100644 (file)
@@ -259,4 +259,11 @@ return [
     'comment_updated_success' => 'Reactie bijgewerkt',
     'comment_delete_confirm' => 'Zeker reactie verwijderen?',
     'comment_in_reply_to' => 'Antwoord op :commentId',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Weet u zeker dat u deze revisie wilt verwijderen?',
+    'revision_delete_success' => 'Revisie verwijderd',
+    'revision_cannot_delete_latest' => 'Kan de laatste revisie niet verwijderen.'
 ];
index 0407b139682ec4becf561920dc633d69264c4b7e..8b53591f6181693cd67740f728697b2e62d636a5 100644 (file)
@@ -257,4 +257,11 @@ return [
     'comment_updated_success' => 'Komentarz zaktualizowany',
     'comment_delete_confirm' => 'Czy na pewno chcesz usunąc ten komentarz?',
     'comment_in_reply_to' => 'W odpowiedzi na :commentId',
+
+     /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Czy na pewno chcesz usunąć tę wersję?',
+    'revision_delete_success' => 'Usunięto wersję',
+    'revision_cannot_delete_latest' => 'Nie można usunąć najnowszej wersji.'
 ];
\ No newline at end of file
index 4dbf9c935776f72c63de6d925b497923b92e08bd..d93b9dff9c90aa81b1898bebf20e173e16387761 100644 (file)
@@ -258,4 +258,11 @@ return [
     'comment_updated_success' => 'Comentário editado',
     'comment_delete_confirm' => 'Você tem certeza de que quer deletar este comentário?',
     'comment_in_reply_to' => 'Em resposta à :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Tem certeza de que deseja excluir esta revisão?',
+    'revision_delete_success' => 'Revisão excluída',
+    'revision_cannot_delete_latest' => 'Não é possível excluir a revisão mais recente.'
 ];
\ No newline at end of file
index a0322d622ea2391cc59525ff7ba7bba94f63d5ab..9c3517bebe51f697ef9d5c21ce751a7b413ae685 100644 (file)
@@ -258,4 +258,11 @@ return [
     'comment_updated_success' => 'Комментарий обновлён',
     'comment_delete_confirm' => 'Вы уверенны, что хотите удалить этот комментарий?',
     'comment_in_reply_to' => 'В ответ на :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Вы действительно хотите удалить эту ревизию?',
+    'revision_delete_success' => 'Редактирование удалено',
+    'revision_cannot_delete_latest' => 'Не удается удалить последнюю версию.'
 ];
\ No newline at end of file
index 8f9a57d1f760dc8c90dbac5939cc71c2f07bc23a..7fbbaf2e2676ed0c0b62477ca8c05cdb02a3aefd 100644 (file)
@@ -232,4 +232,11 @@ return [
     'comments' => 'Komentáre',
     'comment_placeholder' => 'Tu zadajte svoje pripomienky',
     'comment_save' => 'Uložiť komentár',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Naozaj chcete túto revíziu odstrániť?',
+    'revision_delete_success' => 'Revízia bola vymazaná',
+    'revision_cannot_delete_latest' => 'Nie je možné vymazať poslednú revíziu.'
 ];
index 3a2d1a2c6cbeaa1bcbca524698305df1b0ccfa90..8c09bd37709708282429c1dc49552ffecd297c47 100644 (file)
@@ -265,4 +265,11 @@ return [
     'comment_updated_success' => 'Kommentaren har uppdaterats',
     'comment_delete_confirm' => 'Är du säker på att du vill ta bort den här kommentaren?',
     'comment_in_reply_to' => 'Som svar på :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => 'Är du säker på att du vill radera den här versionen?',
+    'revision_delete_success' => 'Revisionen raderad',
+    'revision_cannot_delete_latest' => 'Det går inte att ta bort den senaste versionen.'
 ];
\ No newline at end of file
index eed6b9532f770d97c665056eb27151042b377f0d..734a47a5a7064598828d60dafbef7432e203d9f9 100644 (file)
@@ -258,4 +258,11 @@ return [
     'comment_updated_success' => '评论已更新',
     'comment_delete_confirm' => '你确定要删除这条评论?',
     'comment_in_reply_to' => '回复 :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => '您确定要删除此修订版吗?',
+    'revision_delete_success' => '修订删除',
+    'revision_cannot_delete_latest' => '无法删除最新版本。'
 ];
index 664917eaa686fa5e56cf16df857877f6e0a02761..1c4d526fd7edb3603f5c0d9595a9f1e7b00333bd 100644 (file)
@@ -259,4 +259,11 @@ return [
     'comment_updated_success' => '評論已更新',
     'comment_delete_confirm' => '你確定要刪除這條評論?',
     'comment_in_reply_to' => '回覆 :commentId',
+
+    /**
+     * Revision
+     */
+    'revision_delete_confirm' => '您確定要刪除此修訂版嗎?',
+    'revision_delete_success' => '修訂刪除',
+    'revision_cannot_delete_latest' => '無法刪除最新版本。'
 ];
index d07dc6fcc60d439bd2742eba7a5f02823a494533..72017467ed133b41f945e72f12d8c59ab8151908 100644 (file)
                                 <td> @if($revision->createdBy) {{ $revision->createdBy->name }} @else {{ trans('common.deleted_user') }} @endif</td>
                                 <td><small>{{ $revision->created_at->format('jS F, Y H:i:s') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
                                 <td>{{ $revision->summary }}</td>
-                                <td>
+                                <td class="actions">
                                     <a href="{{ $revision->getUrl('changes') }}" target="_blank">{{ trans('entities.pages_revisions_changes') }}</a>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
 
+
                                     @if ($index === 0)
                                         <a target="_blank" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
                                     @else
                                         <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
                                         <span class="text-muted">&nbsp;|&nbsp;</span>
                                         <a href="{{ $revision->getUrl('restore') }}">{{ trans('entities.pages_revisions_restore') }}</a>
+                                        <span class="text-muted">&nbsp;|&nbsp;</span>
+                                        <div dropdown class="dropdown-container">
+                                            <a dropdown-toggle>{{ trans('common.delete') }}</a>
+                                            <ul>
+                                                <li class="padded"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
+                                                <li>
+                                                    <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
+                                                        {!! csrf_field() !!}
+                                                        <input type="hidden" name="_method" value="DELETE">
+                                                        <button type="submit" class="text-button neg">@icon('delete'){{ trans('common.delete') }}</button>
+                                                    </form>
+                                                </li>
+                                            </ul>
+                                        </div>
                                     @endif
                                 </td>
                             </tr>
index 272aa3dc110b0237cad5e4dd444f3b5fc6f769ec..0b9382f59be427f88b6c235c55ab075d87dce1e3 100644 (file)
@@ -19,4 +19,4 @@
         color: {{ setting('app-color') }};
         fill: {{ setting('app-color') }};
     }
-</style>
\ No newline at end of file
+</style>
index c4e7469fee69a598fc81256525a9968a757b35de..70cb3c130a8a4a496d4e7264248d0bcb757bdea5 100644 (file)
@@ -61,6 +61,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}', 'PageController@showRevision');
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}/changes', 'PageController@showRevisionChanges');
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}/restore', 'PageController@restoreRevision');
+        Route::delete('/{bookSlug}/page/{pageSlug}/revisions/{revId}/delete', 'PageController@destroyRevision');
 
         // Chapters
         Route::get('/{bookSlug}/chapter/{chapterSlug}/create-page', 'PageController@create');
@@ -79,7 +80,6 @@ Route::group(['middleware' => 'auth'], function () {
         Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@restrict');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
         Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy');
-
     });
 
     // User Profile routes
index beebc7adf4e9682667fa9044a73e3579f4ba70e5..08b379107f3bf049c6b26f8efc3a48662ba9c755 100644 (file)
@@ -11,7 +11,6 @@ class PageRevisionTest extends TestCase
     {
         $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);
 
@@ -22,11 +21,43 @@ class PageRevisionTest extends TestCase
     {
         $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);
+        $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);
     }
 
+    public function test_revision_deletion() {
+        $page = Page::first();
+        $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
+
+        $page = Page::find($page->id);
+        $this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
+
+        $page = Page::find($page->id);
+        $beforeRevisionCount = $page->revisions->count();
+
+        // Delete the first revision
+        $revision = $page->revisions->get(1);
+        $resp = $this->asEditor()->delete($revision->getUrl('/delete/'));
+        $resp->assertStatus(200);
+
+        $page = Page::find($page->id);
+        $afterRevisionCount = $page->revisions->count();
+
+        $this->assertTrue($beforeRevisionCount === ($afterRevisionCount + 1));
+
+        // Try to delete the latest revision
+        $beforeRevisionCount = $page->revisions->count();
+        $currentRevision = $page->getCurrentRevision();
+        $resp = $this->asEditor()->delete($currentRevision->getUrl('/delete/'));
+        $resp->assertStatus(400);
+
+        $page = Page::find($page->id);
+        $afterRevisionCount = $page->revisions->count();
+        $this->assertTrue($beforeRevisionCount === $afterRevisionCount);
+    }
 }
\ No newline at end of file