Added ability to copy a page

In 'More' menu alongside move.
Allows you to move if you have permission to create within the new
target parent.
Closes #673
This commit is contained in:
Dan Brown
2018-04-14 18:00:16 +01:00
parent d34b91f2c9
commit 0f7b0ad45a
17 changed files with 243 additions and 57 deletions

View File

@ -197,8 +197,8 @@ class Entity extends Ownable
* @param $path * @param $path
* @return string * @return string
*/ */
public function getUrl($path) public function getUrl($path = '/')
{ {
return '/'; return $path;
} }
} }

View File

@ -592,12 +592,70 @@ class PageController extends Controller
return redirect($page->getUrl()); return redirect($page->getUrl());
} }
/**
* Show the view to copy a page.
* @param string $bookSlug
* @param string $pageSlug
* @return mixed
* @throws NotFoundException
*/
public function showCopy($bookSlug, $pageSlug)
{
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
$this->checkOwnablePermission('page-update', $page);
session()->flashInput(['name' => $page->name]);
return view('pages/copy', [
'book' => $page->book,
'page' => $page
]);
}
/**
* Create a copy of a page within the requested target destination.
* @param string $bookSlug
* @param string $pageSlug
* @param Request $request
* @return mixed
* @throws NotFoundException
*/
public function copy($bookSlug, $pageSlug, Request $request)
{
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
$this->checkOwnablePermission('page-update', $page);
$entitySelection = $request->get('entity_selection', null);
if ($entitySelection === null || $entitySelection === '') {
$parent = $page->chapter ? $page->chapter : $page->book;
} else {
$stringExploded = explode(':', $entitySelection);
$entityType = $stringExploded[0];
$entityId = intval($stringExploded[1]);
try {
$parent = $this->entityRepo->getById($entityType, $entityId);
} catch (\Exception $e) {
session()->flash(trans('entities.selected_book_chapter_not_found'));
return redirect()->back();
}
}
$this->checkOwnablePermission('page-create', $parent);
$pageCopy = $this->entityRepo->copyPage($page, $parent, $request->get('name', ''));
Activity::add($pageCopy, 'page_create', $pageCopy->book->id);
session()->flash('success', trans('entities.pages_copy_success'));
return redirect($pageCopy->getUrl());
}
/** /**
* Set the permissions for this page. * Set the permissions for this page.
* @param string $bookSlug * @param string $bookSlug
* @param string $pageSlug * @param string $pageSlug
* @param Request $request * @param Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws NotFoundException
*/ */
public function restrict($bookSlug, $pageSlug, Request $request) public function restrict($bookSlug, $pageSlug, Request $request)
{ {

View File

@ -89,16 +89,17 @@ class SearchController extends Controller
{ {
$entityTypes = $request->filled('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']); $entityTypes = $request->filled('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']);
$searchTerm = $request->get('term', false); $searchTerm = $request->get('term', false);
$permission = $request->get('permission', 'view');
// Search for entities otherwise show most popular // Search for entities otherwise show most popular
if ($searchTerm !== false) { if ($searchTerm !== false) {
$searchTerm .= ' {type:'. implode('|', $entityTypes->toArray()) .'}'; $searchTerm .= ' {type:'. implode('|', $entityTypes->toArray()) .'}';
$entities = $this->searchService->searchEntities($searchTerm)['results']; $entities = $this->searchService->searchEntities($searchTerm, 'all', 1, 20, $permission)['results'];
} else { } else {
$entityNames = $entityTypes->map(function ($type) { $entityNames = $entityTypes->map(function ($type) {
return 'BookStack\\' . ucfirst($type); return 'BookStack\\' . ucfirst($type);
})->toArray(); })->toArray();
$entities = $this->viewService->getPopular(20, 0, $entityNames); $entities = $this->viewService->getPopular(20, 0, $entityNames, $permission);
} }
return view('search/entity-ajax-list', ['entities' => $entities]); return view('search/entity-ajax-list', ['entities' => $entities]);

View File

@ -593,6 +593,30 @@ class EntityRepo
return $slug; return $slug;
} }
/**
* Get a new draft page instance.
* @param Book $book
* @param Chapter|bool $chapter
* @return Page
*/
public function getDraftPage(Book $book, $chapter = false)
{
$page = $this->page->newInstance();
$page->name = trans('entities.pages_initial_name');
$page->created_by = user()->id;
$page->updated_by = user()->id;
$page->draft = true;
if ($chapter) {
$page->chapter_id = $chapter->id;
}
$book->pages()->save($page);
$page = $this->page->find($page->id);
$this->permissionService->buildJointPermissionsForEntity($page);
return $page;
}
/** /**
* Publish a draft page to make it a normal page. * Publish a draft page to make it a normal page.
* Sets the slug and updates the content. * Sets the slug and updates the content.
@ -621,6 +645,43 @@ class EntityRepo
return $draftPage; return $draftPage;
} }
/**
* Create a copy of a page in a new location with a new name.
* @param Page $page
* @param Entity $newParent
* @param string $newName
* @return Page
*/
public function copyPage(Page $page, Entity $newParent, $newName = '')
{
$newBook = $newParent->isA('book') ? $newParent : $newParent->book;
$newChapter = $newParent->isA('chapter') ? $newParent : null;
$copyPage = $this->getDraftPage($newBook, $newChapter);
$pageData = $page->getAttributes();
// Update name
if (!empty($newName)) {
$pageData['name'] = $newName;
}
// Copy tags from previous page if set
if ($page->tags) {
$pageData['tags'] = [];
foreach ($page->tags as $tag) {
$pageData['tags'][] = ['name' => $tag->name, 'value' => $tag->value];
}
}
// Set priority
if ($newParent->isA('chapter')) {
$pageData['priority'] = $this->getNewChapterPriority($newParent);
} else {
$pageData['priority'] = $this->getNewBookPriority($newParent);
}
return $this->publishPageDraft($copyPage, $pageData);
}
/** /**
* Saves a page revision into the system. * Saves a page revision into the system.
* @param Page $page * @param Page $page
@ -805,30 +866,6 @@ class EntityRepo
return strip_tags($html); return strip_tags($html);
} }
/**
* Get a new draft page instance.
* @param Book $book
* @param Chapter|bool $chapter
* @return Page
*/
public function getDraftPage(Book $book, $chapter = false)
{
$page = $this->page->newInstance();
$page->name = trans('entities.pages_initial_name');
$page->created_by = user()->id;
$page->updated_by = user()->id;
$page->draft = true;
if ($chapter) {
$page->chapter_id = $chapter->id;
}
$book->pages()->save($page);
$page = $this->page->find($page->id);
$this->permissionService->buildJointPermissionsForEntity($page);
return $page;
}
/** /**
* Search for image usage within page content. * Search for image usage within page content.
* @param $imageString * @param $imageString

View File

@ -630,16 +630,17 @@ class PermissionService
* @param string $tableName * @param string $tableName
* @param string $entityIdColumn * @param string $entityIdColumn
* @param string $entityTypeColumn * @param string $entityTypeColumn
* @param string $action
* @return mixed * @return mixed
*/ */
public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn) public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn, $action = 'view')
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->clean(); $this->clean();
return $query; return $query;
} }
$this->currentAction = 'view'; $this->currentAction = $action;
$tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn]; $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
$q = $query->where(function ($query) use ($tableDetails) { $q = $query->where(function ($query) use ($tableDetails) {

View File

@ -67,7 +67,7 @@ class SearchService
* @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed. * @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
* @return array[int, Collection]; * @return array[int, Collection];
*/ */
public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20) public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view')
{ {
$terms = $this->parseSearchString($searchString); $terms = $this->parseSearchString($searchString);
$entityTypes = array_keys($this->entities); $entityTypes = array_keys($this->entities);
@ -87,8 +87,8 @@ class SearchService
if (!in_array($entityType, $entityTypes)) { if (!in_array($entityType, $entityTypes)) {
continue; continue;
} }
$search = $this->searchEntityTable($terms, $entityType, $page, $count); $search = $this->searchEntityTable($terms, $entityType, $page, $count, $action);
$entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, true); $entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, $action, true);
if ($entityTotal > $page * $count) { if ($entityTotal > $page * $count) {
$hasMore = true; $hasMore = true;
} }
@ -147,12 +147,13 @@ class SearchService
* @param string $entityType * @param string $entityType
* @param int $page * @param int $page
* @param int $count * @param int $count
* @param string $action
* @param bool $getCount Return the total count of the search * @param bool $getCount Return the total count of the search
* @return \Illuminate\Database\Eloquent\Collection|int|static[] * @return \Illuminate\Database\Eloquent\Collection|int|static[]
*/ */
public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $getCount = false) public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $action = 'view', $getCount = false)
{ {
$query = $this->buildEntitySearchQuery($terms, $entityType); $query = $this->buildEntitySearchQuery($terms, $entityType, $action);
if ($getCount) { if ($getCount) {
return $query->count(); return $query->count();
} }
@ -165,9 +166,10 @@ class SearchService
* Create a search query for an entity * Create a search query for an entity
* @param array $terms * @param array $terms
* @param string $entityType * @param string $entityType
* @param string $action
* @return \Illuminate\Database\Eloquent\Builder * @return \Illuminate\Database\Eloquent\Builder
*/ */
protected function buildEntitySearchQuery($terms, $entityType = 'page') protected function buildEntitySearchQuery($terms, $entityType = 'page', $action = 'view')
{ {
$entity = $this->getEntity($entityType); $entity = $this->getEntity($entityType);
$entitySelect = $entity->newQuery(); $entitySelect = $entity->newQuery();
@ -212,7 +214,7 @@ class SearchService
} }
} }
return $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view'); return $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, $action);
} }

View File

@ -51,11 +51,13 @@ class ViewService
* @param int $count * @param int $count
* @param int $page * @param int $page
* @param bool|false|array $filterModel * @param bool|false|array $filterModel
* @param string $action - used for permission checking
* @return
*/ */
public function getPopular($count = 10, $page = 0, $filterModel = false) public function getPopular($count = 10, $page = 0, $filterModel = false, $action = 'view')
{ {
$skipCount = $count * $page; $skipCount = $count * $page;
$query = $this->permissionService->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type') $query = $this->permissionService->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type', $action)
->select('*', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count')) ->select('*', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
->groupBy('viewable_id', 'viewable_type') ->groupBy('viewable_id', 'viewable_type')
->orderBy('view_count', 'desc'); ->orderBy('view_count', 'desc');

5
package-lock.json generated
View File

@ -6825,11 +6825,6 @@
} }
} }
}, },
"moment": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz",
"integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ=="
},
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

View File

@ -7,7 +7,8 @@ class EntitySelector {
this.lastClick = 0; this.lastClick = 0;
let entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter'; let entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter';
this.searchUrl = window.baseUrl(`/ajax/search/entities?types=${encodeURIComponent(entityTypes)}`); let entityPermission = elem.hasAttribute('entity-permission') ? elem.getAttribute('entity-permission') : 'view';
this.searchUrl = window.baseUrl(`/ajax/search/entities?types=${encodeURIComponent(entityTypes)}&permission=${encodeURIComponent(entityPermission)}`);
this.input = elem.querySelector('[entity-selector-input]'); this.input = elem.querySelector('[entity-selector-input]');
this.searchInput = elem.querySelector('[entity-selector-search]'); this.searchInput = elem.querySelector('[entity-selector-search]');
@ -68,7 +69,6 @@ class EntitySelector {
onClick(event) { onClick(event) {
let t = event.target; let t = event.target;
console.log('click', t);
if (t.matches('.entity-list-item *')) { if (t.matches('.entity-list-item *')) {
event.preventDefault(); event.preventDefault();

View File

@ -31,6 +31,7 @@ return [
'edit' => 'Edit', 'edit' => 'Edit',
'sort' => 'Sort', 'sort' => 'Sort',
'move' => 'Move', 'move' => 'Move',
'copy' => 'Copy',
'reply' => 'Reply', 'reply' => 'Reply',
'delete' => 'Delete', 'delete' => 'Delete',
'search' => 'Search', 'search' => 'Search',

View File

@ -166,6 +166,9 @@ return [
'pages_not_in_chapter' => 'Page is not in a chapter', 'pages_not_in_chapter' => 'Page is not in a chapter',
'pages_move' => 'Move Page', 'pages_move' => 'Move Page',
'pages_move_success' => 'Page moved to ":parentName"', 'pages_move_success' => 'Page moved to ":parentName"',
'pages_copy' => 'Copy Page',
'pages_copy_desination' => 'Copy Destination',
'pages_copy_success' => 'Page successfully copied',
'pages_permissions' => 'Page Permissions', 'pages_permissions' => 'Page Permissions',
'pages_permissions_success' => 'Page permissions updated', 'pages_permissions_success' => 'Page permissions updated',
'pages_revision' => 'Revision', 'pages_revision' => 'Revision',

View File

@ -30,9 +30,9 @@
</div> </div>
</div> </div>
<div class="form-group" collapsible id="logo-control"> <div class="form-group" collapsible id="tags-control">
<div class="collapse-title text-primary" collapsible-trigger> <div class="collapse-title text-primary" collapsible-trigger>
<label for="user-avatar">{{ trans('entities.book_tags') }}</label> <label for="tag-manager">{{ trans('entities.book_tags') }}</label>
</div> </div>
<div class="collapse-content" collapsible-content> <div class="collapse-content" collapsible-content>
@include('components.tag-manager', ['entity' => isset($book)?$book:null, 'entityType' => 'chapter']) @include('components.tag-manager', ['entity' => isset($book)?$book:null, 'entityType' => 'chapter'])

View File

@ -1,5 +1,5 @@
<div class="form-group"> <div class="form-group">
<div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}"> <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}" entity-permission="{{ $entityPermission or 'view' }}">
<input type="hidden" entity-selector-input name="{{$name}}" value=""> <input type="hidden" entity-selector-input name="{{$name}}" value="">
<input type="text" placeholder="{{ trans('common.search') }}" entity-selector-search> <input type="text" placeholder="{{ trans('common.search') }}" entity-selector-search>
<div class="text-center loading" entity-selector-loading>@include('partials.loading-icon')</div> <div class="text-center loading" entity-selector-loading>@include('partials.loading-icon')</div>

View File

@ -0,0 +1,43 @@
@extends('simple-layout')
@section('toolbar')
<div class="col-sm-12 faded">
@include('pages._breadcrumbs', ['page' => $page])
</div>
@stop
@section('body')
<div class="container small">
<p>&nbsp;</p>
<div class="card">
<h3>@icon('copy') {{ trans('entities.pages_copy') }}</h3>
<div class="body">
<form action="{{ $page->getUrl('/copy') }}" method="POST">
{!! csrf_field() !!}
<div class="form-group title-input">
<label for="name">{{ trans('common.name') }}</label>
@include('form/text', ['name' => 'name'])
</div>
<div class="form-group" collapsible>
<div class="collapse-title text-primary" collapsible-trigger>
<label for="entity_selection">{{ trans('entities.pages_copy_desination') }}</label>
</div>
<div class="collapse-content" collapsible-content>
@include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter', 'entityPermission' => 'page-create'])
</div>
</div>
<div class="form-group text-right">
<a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.pages_copy') }}</button>
</div>
</form>
</div>
</div>
</div>
@stop

View File

@ -22,6 +22,7 @@
<a dropdown-toggle class="text-primary text-button">@icon('more') {{ trans('common.more') }}</a> <a dropdown-toggle class="text-primary text-button">@icon('more') {{ trans('common.more') }}</a>
<ul> <ul>
@if(userCan('page-update', $page)) @if(userCan('page-update', $page))
<li><a href="{{ $page->getUrl('/copy') }}" class="text-primary" >@icon('copy'){{ trans('common.copy') }}</a></li>
<li><a href="{{ $page->getUrl('/move') }}" class="text-primary" >@icon('folder'){{ trans('common.move') }}</a></li> <li><a href="{{ $page->getUrl('/move') }}" class="text-primary" >@icon('folder'){{ trans('common.move') }}</a></li>
<li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary">@icon('history'){{ trans('entities.revisions') }}</a></li> <li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary">@icon('history'){{ trans('entities.revisions') }}</a></li>
@endif @endif

View File

@ -47,6 +47,8 @@ Route::group(['middleware' => 'auth'], function () {
Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit'); Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove'); Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove');
Route::put('/{bookSlug}/page/{pageSlug}/move', 'PageController@move'); Route::put('/{bookSlug}/page/{pageSlug}/move', 'PageController@move');
Route::get('/{bookSlug}/page/{pageSlug}/copy', 'PageController@showCopy');
Route::post('/{bookSlug}/page/{pageSlug}/copy', 'PageController@copy');
Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete'); Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
Route::get('/{bookSlug}/draft/{pageId}/delete', 'PageController@showDeleteDraft'); Route::get('/{bookSlug}/draft/{pageId}/delete', 'PageController@showDeleteDraft');
Route::get('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@showRestrict'); Route::get('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@showRestrict');

View File

@ -1,6 +1,7 @@
<?php namespace Tests; <?php namespace Tests;
use BookStack\Book; use BookStack\Book;
use BookStack\Chapter;
use BookStack\Page; use BookStack\Page;
use BookStack\Repos\EntityRepo; use BookStack\Repos\EntityRepo;
@ -11,7 +12,7 @@ class SortTest extends TestCase
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
$this->book = \BookStack\Book::first(); $this->book = Book::first();
} }
public function test_drafts_do_not_show_up() public function test_drafts_do_not_show_up()
@ -29,9 +30,9 @@ class SortTest extends TestCase
public function test_page_move() public function test_page_move()
{ {
$page = \BookStack\Page::first(); $page = Page::first();
$currentBook = $page->book; $currentBook = $page->book;
$newBook = \BookStack\Book::where('id', '!=', $currentBook->id)->first(); $newBook = Book::where('id', '!=', $currentBook->id)->first();
$resp = $this->asAdmin()->get($page->getUrl() . '/move'); $resp = $this->asAdmin()->get($page->getUrl() . '/move');
$resp->assertSee('Move Page'); $resp->assertSee('Move Page');
@ -39,7 +40,7 @@ class SortTest extends TestCase
$movePageResp = $this->put($page->getUrl() . '/move', [ $movePageResp = $this->put($page->getUrl() . '/move', [
'entity_selection' => 'book:' . $newBook->id 'entity_selection' => 'book:' . $newBook->id
]); ]);
$page = \BookStack\Page::find($page->id); $page = Page::find($page->id);
$movePageResp->assertRedirect($page->getUrl()); $movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
@ -51,10 +52,10 @@ class SortTest extends TestCase
public function test_chapter_move() public function test_chapter_move()
{ {
$chapter = \BookStack\Chapter::first(); $chapter = Chapter::first();
$currentBook = $chapter->book; $currentBook = $chapter->book;
$pageToCheck = $chapter->pages->first(); $pageToCheck = $chapter->pages->first();
$newBook = \BookStack\Book::where('id', '!=', $currentBook->id)->first(); $newBook = Book::where('id', '!=', $currentBook->id)->first();
$chapterMoveResp = $this->asAdmin()->get($chapter->getUrl() . '/move'); $chapterMoveResp = $this->asAdmin()->get($chapter->getUrl() . '/move');
$chapterMoveResp->assertSee('Move Chapter'); $chapterMoveResp->assertSee('Move Chapter');
@ -63,7 +64,7 @@ class SortTest extends TestCase
'entity_selection' => 'book:' . $newBook->id 'entity_selection' => 'book:' . $newBook->id
]); ]);
$chapter = \BookStack\Chapter::find($chapter->id); $chapter = Chapter::find($chapter->id);
$moveChapterResp->assertRedirect($chapter->getUrl()); $moveChapterResp->assertRedirect($chapter->getUrl());
$this->assertTrue($chapter->book->id === $newBook->id, 'Chapter Book is now the new book'); $this->assertTrue($chapter->book->id === $newBook->id, 'Chapter Book is now the new book');
@ -71,7 +72,7 @@ class SortTest extends TestCase
$newBookResp->assertSee('moved chapter'); $newBookResp->assertSee('moved chapter');
$newBookResp->assertSee($chapter->name); $newBookResp->assertSee($chapter->name);
$pageToCheck = \BookStack\Page::find($pageToCheck->id); $pageToCheck = Page::find($pageToCheck->id);
$this->assertTrue($pageToCheck->book_id === $newBook->id, 'Chapter child page\'s book id has changed to the new book'); $this->assertTrue($pageToCheck->book_id === $newBook->id, 'Chapter child page\'s book id has changed to the new book');
$pageCheckResp = $this->get($pageToCheck->getUrl()); $pageCheckResp = $this->get($pageToCheck->getUrl());
$pageCheckResp->assertSee($newBook->name); $pageCheckResp->assertSee($newBook->name);
@ -120,4 +121,43 @@ class SortTest extends TestCase
$checkResp->assertSee($newBook->name); $checkResp->assertSee($newBook->name);
} }
public function test_page_copy()
{
$page = Page::first();
$currentBook = $page->book;
$newBook = Book::where('id', '!=', $currentBook->id)->first();
$resp = $this->asEditor()->get($page->getUrl('/copy'));
$resp->assertSee('Copy Page');
$movePageResp = $this->post($page->getUrl('/copy'), [
'entity_selection' => 'book:' . $newBook->id,
'name' => 'My copied test page'
]);
$pageCopy = Page::where('name', '=', 'My copied test page')->first();
$movePageResp->assertRedirect($pageCopy->getUrl());
$this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
}
public function test_page_copy_with_no_destination()
{
$page = Page::first();
$currentBook = $page->book;
$resp = $this->asEditor()->get($page->getUrl('/copy'));
$resp->assertSee('Copy Page');
$movePageResp = $this->post($page->getUrl('/copy'), [
'name' => 'My copied test page'
]);
$pageCopy = Page::where('name', '=', 'My copied test page')->first();
$movePageResp->assertRedirect($pageCopy->getUrl());
$this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
$this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
}
} }