diff --git a/app/Exceptions/BadRequestException.php b/app/Exceptions/BadRequestException.php new file mode 100644 index 000000000..b0353bad4 --- /dev/null +++ b/app/Exceptions/BadRequestException.php @@ -0,0 +1,14 @@ +<?php namespace BookStack\Exceptions; + +class BadRequestException extends PrettyException +{ + + /** + * BadRequestException constructor. + * @param string $message + */ + public function __construct($message = 'Bad request') + { + parent::__construct($message, 400); + } +} diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 25a0503eb..5979ceca8 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -2,6 +2,7 @@ use Activity; use BookStack\Exceptions\NotFoundException; +use BookStack\Exceptions\BadRequestException; use BookStack\Repos\EntityRepo; use BookStack\Repos\UserRepo; use BookStack\Services\ExportService; @@ -454,6 +455,38 @@ 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 $revisionId + * @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-update', $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 + $current = $revision->getCurrent(); + + // Check if its the latest revision, cannot delete latest revision. + if (intval($current->id) === intval($revId)) { + throw new BadRequestException("Cannot delete the current revision #{$revId}"); + } + + $revision->delete(); + return view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]); + } + /** * Exports a page to a PDF. * https://github.com/barryvdh/laravel-dompdf diff --git a/app/PageRevision.php b/app/PageRevision.php index ffcc4f9d2..8e5c16bc9 100644 --- a/app/PageRevision.php +++ b/app/PageRevision.php @@ -48,6 +48,18 @@ class PageRevision extends Model return null; } + /** + * Get the current revision for the same page if existing + * @return \BookStack\PageRevision|null + */ + public function getCurrent() + { + if ($id = static::where('page_id', '=', $this->page_id)->max('id')) { + return static::find($id); + } + return null; + } + /** * Allows checking of the exact class, Used to check entity type. * Included here to align with entities in similar use cases. diff --git a/resources/assets/sass/_buttons.scss b/resources/assets/sass/_buttons.scss index 2c20c3f41..7738eb4ba 100644 --- a/resources/assets/sass/_buttons.scss +++ b/resources/assets/sass/_buttons.scss @@ -119,6 +119,11 @@ $button-border-radius: 2px; &.neg { color: $negative; } + &.link { + &:hover { + text-decoration: underline; + } + } } .button-group { diff --git a/resources/assets/sass/_lists.scss b/resources/assets/sass/_lists.scss index 3338b3938..e8d131b52 100644 --- a/resources/assets/sass/_lists.scss +++ b/resources/assets/sass/_lists.scss @@ -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; } diff --git a/resources/assets/sass/_tables.scss b/resources/assets/sass/_tables.scss index 38b044268..ec24e2fa6 100644 --- a/resources/assets/sass/_tables.scss +++ b/resources/assets/sass/_tables.scss @@ -41,6 +41,9 @@ table.table { .text-center { text-align: center; } + td.actions { + overflow: visible; + } } table.no-style { diff --git a/resources/views/pages/revisions.blade.php b/resources/views/pages/revisions.blade.php index d07dc6fcc..99be26153 100644 --- a/resources/views/pages/revisions.blade.php +++ b/resources/views/pages/revisions.blade.php @@ -36,16 +36,31 @@ <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"> | </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"> | </span> <a href="{{ $revision->getUrl('restore') }}">{{ trans('entities.pages_revisions_restore') }}</a> + <span class="text-muted"> | </span> + <div dropdown class="dropdown-container"> + <button type="button" dropdown-toggle class="text-button link">{{ trans('common.delete') }}</button> + <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> diff --git a/resources/views/partials/custom-styles.blade.php b/resources/views/partials/custom-styles.blade.php index 272aa3dc1..0b9382f59 100644 --- a/resources/views/partials/custom-styles.blade.php +++ b/resources/views/partials/custom-styles.blade.php @@ -19,4 +19,4 @@ color: {{ setting('app-color') }}; fill: {{ setting('app-color') }}; } -</style> \ No newline at end of file +</style> diff --git a/routes/web.php b/routes/web.php index c4e7469fe..70cb3c130 100644 --- a/routes/web.php +++ b/routes/web.php @@ -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