mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-08-26 20:22:18 +00:00
Merge branch 'master' of https://github.com/BookStackApp/BookStack
This commit is contained in:
commit
89de328439
@ -1,7 +1,7 @@
|
||||
<?php namespace BookStack\Auth;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Uploads\Image;
|
||||
use Exception;
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace BookStack\Entities;
|
||||
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Uploads\ImageService;
|
||||
|
||||
class ExportService
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?php namespace BookStack\Entities;
|
||||
<?php namespace BookStack\Entities\Repos;
|
||||
|
||||
use BookStack\Actions\TagRepo;
|
||||
use BookStack\Actions\ViewService;
|
||||
use BookStack\Auth\Permissions\PermissionService;
|
||||
use BookStack\Auth\User;
|
||||
use BookStack\Entities\Book;
|
||||
use BookStack\Entities\Bookshelf;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Entity;
|
||||
use BookStack\Entities\EntityProvider;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\SearchService;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Exceptions\NotifyException;
|
||||
use BookStack\Uploads\AttachmentService;
|
||||
use Carbon\Carbon;
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@ -154,26 +159,6 @@ class EntityRepo
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search through page revisions and retrieve the last page in the
|
||||
* current book that has a slug equal to the one given.
|
||||
* @param string $pageSlug
|
||||
* @param string $bookSlug
|
||||
* @return null|Page
|
||||
*/
|
||||
public function getPageByOldSlug($pageSlug, $bookSlug)
|
||||
{
|
||||
$revision = $this->entityProvider->pageRevision->where('slug', '=', $pageSlug)
|
||||
->whereHas('page', function ($query) {
|
||||
$this->permissionService->enforceEntityRestrictions('page', $query);
|
||||
})
|
||||
->where('type', '=', 'version')
|
||||
->where('book_slug', '=', $bookSlug)
|
||||
->orderBy('created_at', 'desc')
|
||||
->with('page')->first();
|
||||
return $revision !== null ? $revision->page : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entities of a type with the given permission, limited by count unless count is false.
|
||||
* @param string $type
|
||||
@ -613,191 +598,6 @@ class EntityRepo
|
||||
return $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new draft page instance.
|
||||
* @param Book $book
|
||||
* @param Chapter|bool $chapter
|
||||
* @return \BookStack\Entities\Page
|
||||
*/
|
||||
public function getDraftPage(Book $book, $chapter = false)
|
||||
{
|
||||
$page = $this->entityProvider->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->entityProvider->page->find($page->id);
|
||||
$this->permissionService->buildJointPermissionsForEntity($page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a draft page to make it a normal page.
|
||||
* Sets the slug and updates the content.
|
||||
* @param Page $draftPage
|
||||
* @param array $input
|
||||
* @return Page
|
||||
*/
|
||||
public function publishPageDraft(Page $draftPage, array $input)
|
||||
{
|
||||
$draftPage->fill($input);
|
||||
|
||||
// Save page tags if present
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
|
||||
}
|
||||
|
||||
$draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
|
||||
$draftPage->html = $this->formatHtml($input['html']);
|
||||
$draftPage->text = $this->pageToPlainText($draftPage);
|
||||
$draftPage->draft = false;
|
||||
$draftPage->revision_count = 1;
|
||||
|
||||
$draftPage->save();
|
||||
$this->savePageRevision($draftPage, trans('entities.pages_initial_revision'));
|
||||
$this->searchService->indexEntity($draftPage);
|
||||
return $draftPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of a page in a new location with a new name.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param \BookStack\Entities\Entity $newParent
|
||||
* @param string $newName
|
||||
* @return \BookStack\Entities\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.
|
||||
* @param Page $page
|
||||
* @param null|string $summary
|
||||
* @return \BookStack\Entities\PageRevision
|
||||
*/
|
||||
public function savePageRevision(Page $page, $summary = null)
|
||||
{
|
||||
$revision = $this->entityProvider->pageRevision->newInstance($page->toArray());
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$revision->markdown = '';
|
||||
}
|
||||
$revision->page_id = $page->id;
|
||||
$revision->slug = $page->slug;
|
||||
$revision->book_slug = $page->book->slug;
|
||||
$revision->created_by = user()->id;
|
||||
$revision->created_at = $page->updated_at;
|
||||
$revision->type = 'version';
|
||||
$revision->summary = $summary;
|
||||
$revision->revision_number = $page->revision_count;
|
||||
$revision->save();
|
||||
|
||||
$revisionLimit = config('app.revision_limit');
|
||||
if ($revisionLimit !== false) {
|
||||
$revisionsToDelete = $this->entityProvider->pageRevision->where('page_id', '=', $page->id)
|
||||
->orderBy('created_at', 'desc')->skip(intval($revisionLimit))->take(10)->get(['id']);
|
||||
if ($revisionsToDelete->count() > 0) {
|
||||
$this->entityProvider->pageRevision->whereIn('id', $revisionsToDelete->pluck('id'))->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a page's html to be tagged correctly
|
||||
* within the system.
|
||||
* @param string $htmlText
|
||||
* @return string
|
||||
*/
|
||||
protected function formatHtml($htmlText)
|
||||
{
|
||||
if ($htmlText == '') {
|
||||
return $htmlText;
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
|
||||
|
||||
$container = $doc->documentElement;
|
||||
$body = $container->childNodes->item(0);
|
||||
$childNodes = $body->childNodes;
|
||||
|
||||
// Ensure no duplicate ids are used
|
||||
$idArray = [];
|
||||
|
||||
foreach ($childNodes as $index => $childNode) {
|
||||
/** @var \DOMElement $childNode */
|
||||
if (get_class($childNode) !== 'DOMElement') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Overwrite id if not a BookStack custom id
|
||||
if ($childNode->hasAttribute('id')) {
|
||||
$id = $childNode->getAttribute('id');
|
||||
if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) {
|
||||
$idArray[] = $id;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
// Create an unique id for the element
|
||||
// Uses the content as a basis to ensure output is the same every time
|
||||
// the same content is passed through.
|
||||
$contentId = 'bkmrk-' . substr(strtolower(preg_replace('/\s+/', '-', trim($childNode->nodeValue))), 0, 20);
|
||||
$newId = urlencode($contentId);
|
||||
$loopIndex = 0;
|
||||
while (in_array($newId, $idArray)) {
|
||||
$newId = urlencode($contentId . '-' . $loopIndex);
|
||||
$loopIndex++;
|
||||
}
|
||||
|
||||
$childNode->setAttribute('id', $newId);
|
||||
$idArray[] = $newId;
|
||||
}
|
||||
|
||||
// Generate inner html as a string
|
||||
$html = '';
|
||||
foreach ($childNodes as $childNode) {
|
||||
$html .= $doc->saveHTML($childNode);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the page for viewing, Parsing and performing features such as page transclusion.
|
||||
* @param Page $page
|
||||
@ -878,17 +678,6 @@ class EntityRepo
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain text version of a page's content.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @return string
|
||||
*/
|
||||
public function pageToPlainText(Page $page)
|
||||
{
|
||||
$html = $this->renderPage($page);
|
||||
return strip_tags($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for image usage within page content.
|
||||
* @param $imageString
|
||||
@ -905,278 +694,6 @@ class EntityRepo
|
||||
return count($pages) > 0 ? $pages : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the headers on the page to get a navigation menu
|
||||
* @param String $pageContent
|
||||
* @return array
|
||||
*/
|
||||
public function getPageNav($pageContent)
|
||||
{
|
||||
if ($pageContent == '') {
|
||||
return [];
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding($pageContent, 'HTML-ENTITIES', 'UTF-8'));
|
||||
$xPath = new DOMXPath($doc);
|
||||
$headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6");
|
||||
|
||||
if (is_null($headers)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tree = collect([]);
|
||||
foreach ($headers as $header) {
|
||||
$text = $header->nodeValue;
|
||||
$tree->push([
|
||||
'nodeName' => strtolower($header->nodeName),
|
||||
'level' => intval(str_replace('h', '', $header->nodeName)),
|
||||
'link' => '#' . $header->getAttribute('id'),
|
||||
'text' => strlen($text) > 30 ? substr($text, 0, 27) . '...' : $text
|
||||
]);
|
||||
}
|
||||
|
||||
// Normalise headers if only smaller headers have been used
|
||||
if (count($tree) > 0) {
|
||||
$minLevel = $tree->pluck('level')->min();
|
||||
$tree = $tree->map(function ($header) use ($minLevel) {
|
||||
$header['level'] -= ($minLevel - 2);
|
||||
return $header;
|
||||
});
|
||||
}
|
||||
return $tree->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a page with any fillable data and saves it into the database.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param int $book_id
|
||||
* @param array $input
|
||||
* @return \BookStack\Entities\Page
|
||||
*/
|
||||
public function updatePage(Page $page, $book_id, $input)
|
||||
{
|
||||
// Hold the old details to compare later
|
||||
$oldHtml = $page->html;
|
||||
$oldName = $page->name;
|
||||
|
||||
// Prevent slug being updated if no name change
|
||||
if ($page->name !== $input['name']) {
|
||||
$page->slug = $this->findSuitableSlug('page', $input['name'], $page->id, $book_id);
|
||||
}
|
||||
|
||||
// Save page tags if present
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($page, $input['tags']);
|
||||
}
|
||||
|
||||
// Update with new details
|
||||
$userId = user()->id;
|
||||
$page->fill($input);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$page->markdown = '';
|
||||
}
|
||||
$page->updated_by = $userId;
|
||||
$page->revision_count++;
|
||||
$page->save();
|
||||
|
||||
// Remove all update drafts for this user & page.
|
||||
$this->userUpdatePageDraftsQuery($page, $userId)->delete();
|
||||
|
||||
// Save a revision after updating
|
||||
if ($oldHtml !== $input['html'] || $oldName !== $input['name'] || $input['summary'] !== null) {
|
||||
$this->savePageRevision($page, $input['summary']);
|
||||
}
|
||||
|
||||
$this->searchService->indexEntity($page);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for getting user update drafts.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param $userId
|
||||
* @return mixed
|
||||
*/
|
||||
protected function userUpdatePageDraftsQuery(Page $page, $userId)
|
||||
{
|
||||
return $this->entityProvider->pageRevision->where('created_by', '=', $userId)
|
||||
->where('type', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a user has a draft version of a particular page or not.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUserGotPageDraft(Page $page, $userId)
|
||||
{
|
||||
return $this->userUpdatePageDraftsQuery($page, $userId)->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest updated draft revision for a particular page and user.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUserPageDraft(Page $page, $userId)
|
||||
{
|
||||
return $this->userUpdatePageDraftsQuery($page, $userId)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification message that informs the user that they are editing a draft page.
|
||||
* @param PageRevision $draft
|
||||
* @return string
|
||||
*/
|
||||
public function getUserPageDraftMessage(PageRevision $draft)
|
||||
{
|
||||
$message = trans('entities.pages_editing_draft_notification', ['timeDiff' => $draft->updated_at->diffForHumans()]);
|
||||
if ($draft->page->updated_at->timestamp <= $draft->updated_at->timestamp) {
|
||||
return $message;
|
||||
}
|
||||
return $message . "\n" . trans('entities.pages_draft_edited_notification');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page is being actively editing.
|
||||
* Checks for edits since last page updated.
|
||||
* Passing in a minuted range will check for edits
|
||||
* within the last x minutes.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param null $minRange
|
||||
* @return bool
|
||||
*/
|
||||
public function isPageEditingActive(Page $page, $minRange = null)
|
||||
{
|
||||
$draftSearch = $this->activePageEditingQuery($page, $minRange);
|
||||
return $draftSearch->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A query to check for active update drafts on a particular page.
|
||||
* @param Page $page
|
||||
* @param null $minRange
|
||||
* @return mixed
|
||||
*/
|
||||
protected function activePageEditingQuery(Page $page, $minRange = null)
|
||||
{
|
||||
$query = $this->entityProvider->pageRevision->where('type', '=', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->where('updated_at', '>', $page->updated_at)
|
||||
->where('created_by', '!=', user()->id)
|
||||
->with('createdBy');
|
||||
|
||||
if ($minRange !== null) {
|
||||
$query = $query->where('updated_at', '>=', Carbon::now()->subMinutes($minRange));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a revision's content back into a page.
|
||||
* @param Page $page
|
||||
* @param Book $book
|
||||
* @param int $revisionId
|
||||
* @return \BookStack\Entities\Page
|
||||
*/
|
||||
public function restorePageRevision(Page $page, Book $book, $revisionId)
|
||||
{
|
||||
$page->revision_count++;
|
||||
$this->savePageRevision($page);
|
||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
$page->fill($revision->toArray());
|
||||
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
$page->updated_by = user()->id;
|
||||
$page->save();
|
||||
$this->searchService->indexEntity($page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save a page update draft.
|
||||
* @param Page $page
|
||||
* @param array $data
|
||||
* @return PageRevision|Page
|
||||
*/
|
||||
public function updatePageDraft(Page $page, $data = [])
|
||||
{
|
||||
// If the page itself is a draft simply update that
|
||||
if ($page->draft) {
|
||||
$page->fill($data);
|
||||
if (isset($data['html'])) {
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
}
|
||||
$page->save();
|
||||
return $page;
|
||||
}
|
||||
|
||||
// Otherwise save the data to a revision
|
||||
$userId = user()->id;
|
||||
$drafts = $this->userUpdatePageDraftsQuery($page, $userId)->get();
|
||||
|
||||
if ($drafts->count() > 0) {
|
||||
$draft = $drafts->first();
|
||||
} else {
|
||||
$draft = $this->entityProvider->pageRevision->newInstance();
|
||||
$draft->page_id = $page->id;
|
||||
$draft->slug = $page->slug;
|
||||
$draft->book_slug = $page->book->slug;
|
||||
$draft->created_by = $userId;
|
||||
$draft->type = 'update_draft';
|
||||
}
|
||||
|
||||
$draft->fill($data);
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$draft->markdown = '';
|
||||
}
|
||||
|
||||
$draft->save();
|
||||
return $draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a notification message concerning the editing activity on a particular page.
|
||||
* @param Page $page
|
||||
* @param null $minRange
|
||||
* @return string
|
||||
*/
|
||||
public function getPageEditingActiveMessage(Page $page, $minRange = null)
|
||||
{
|
||||
$pageDraftEdits = $this->activePageEditingQuery($page, $minRange)->get();
|
||||
|
||||
$userMessage = $pageDraftEdits->count() > 1 ? trans('entities.pages_draft_edit_active.start_a', ['count' => $pageDraftEdits->count()]): trans('entities.pages_draft_edit_active.start_b', ['userName' => $pageDraftEdits->first()->createdBy->name]);
|
||||
$timeMessage = $minRange === null ? trans('entities.pages_draft_edit_active.time_a') : trans('entities.pages_draft_edit_active.time_b', ['minCount'=>$minRange]);
|
||||
return trans('entities.pages_draft_edit_active.message', ['start' => $userMessage, 'time' => $timeMessage]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the page's parent to the given entity.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param \BookStack\Entities\Entity $parent
|
||||
*/
|
||||
public function changePageParent(Page $page, Entity $parent)
|
||||
{
|
||||
$book = $parent->isA('book') ? $parent : $parent->book;
|
||||
$page->chapter_id = $parent->isA('chapter') ? $parent->id : 0;
|
||||
$page->save();
|
||||
if ($page->book->id !== $book->id) {
|
||||
$page = $this->changeBook('page', $book->id, $page);
|
||||
}
|
||||
$page->load('book');
|
||||
$this->permissionService->buildJointPermissionsForEntity($book);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a bookshelf instance
|
||||
* @param \BookStack\Entities\Bookshelf $shelf
|
508
app/Entities/Repos/PageRepo.php
Normal file
508
app/Entities/Repos/PageRepo.php
Normal file
@ -0,0 +1,508 @@
|
||||
<?php namespace BookStack\Entities\Repos;
|
||||
|
||||
use BookStack\Entities\Book;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Entity;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\PageRevision;
|
||||
use Carbon\Carbon;
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
|
||||
class PageRepo extends EntityRepo
|
||||
{
|
||||
|
||||
/**
|
||||
* Get page by slug.
|
||||
* @param string $pageSlug
|
||||
* @param string $bookSlug
|
||||
* @return Page
|
||||
* @throws \BookStack\Exceptions\NotFoundException
|
||||
*/
|
||||
public function getPageBySlug(string $pageSlug, string $bookSlug)
|
||||
{
|
||||
return $this->getBySlug('page', $pageSlug, $bookSlug);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through page revisions and retrieve the last page in the
|
||||
* current book that has a slug equal to the one given.
|
||||
* @param string $pageSlug
|
||||
* @param string $bookSlug
|
||||
* @return null|Page
|
||||
*/
|
||||
public function getPageByOldSlug(string $pageSlug, string $bookSlug)
|
||||
{
|
||||
$revision = $this->entityProvider->pageRevision->where('slug', '=', $pageSlug)
|
||||
->whereHas('page', function ($query) {
|
||||
$this->permissionService->enforceEntityRestrictions('page', $query);
|
||||
})
|
||||
->where('type', '=', 'version')
|
||||
->where('book_slug', '=', $bookSlug)
|
||||
->orderBy('created_at', 'desc')
|
||||
->with('page')->first();
|
||||
return $revision !== null ? $revision->page : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a page with any fillable data and saves it into the database.
|
||||
* @param Page $page
|
||||
* @param int $book_id
|
||||
* @param array $input
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updatePage(Page $page, int $book_id, array $input)
|
||||
{
|
||||
// Hold the old details to compare later
|
||||
$oldHtml = $page->html;
|
||||
$oldName = $page->name;
|
||||
|
||||
// Prevent slug being updated if no name change
|
||||
if ($page->name !== $input['name']) {
|
||||
$page->slug = $this->findSuitableSlug('page', $input['name'], $page->id, $book_id);
|
||||
}
|
||||
|
||||
// Save page tags if present
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($page, $input['tags']);
|
||||
}
|
||||
|
||||
// Update with new details
|
||||
$userId = user()->id;
|
||||
$page->fill($input);
|
||||
$page->html = $this->formatHtml($input['html']);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$page->markdown = '';
|
||||
}
|
||||
$page->updated_by = $userId;
|
||||
$page->revision_count++;
|
||||
$page->save();
|
||||
|
||||
// Remove all update drafts for this user & page.
|
||||
$this->userUpdatePageDraftsQuery($page, $userId)->delete();
|
||||
|
||||
// Save a revision after updating
|
||||
if ($oldHtml !== $input['html'] || $oldName !== $input['name'] || $input['summary'] !== null) {
|
||||
$this->savePageRevision($page, $input['summary']);
|
||||
}
|
||||
|
||||
$this->searchService->indexEntity($page);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a page revision into the system.
|
||||
* @param Page $page
|
||||
* @param null|string $summary
|
||||
* @return PageRevision
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function savePageRevision(Page $page, string $summary = null)
|
||||
{
|
||||
$revision = $this->entityProvider->pageRevision->newInstance($page->toArray());
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$revision->markdown = '';
|
||||
}
|
||||
$revision->page_id = $page->id;
|
||||
$revision->slug = $page->slug;
|
||||
$revision->book_slug = $page->book->slug;
|
||||
$revision->created_by = user()->id;
|
||||
$revision->created_at = $page->updated_at;
|
||||
$revision->type = 'version';
|
||||
$revision->summary = $summary;
|
||||
$revision->revision_number = $page->revision_count;
|
||||
$revision->save();
|
||||
|
||||
$revisionLimit = config('app.revision_limit');
|
||||
if ($revisionLimit !== false) {
|
||||
$revisionsToDelete = $this->entityProvider->pageRevision->where('page_id', '=', $page->id)
|
||||
->orderBy('created_at', 'desc')->skip(intval($revisionLimit))->take(10)->get(['id']);
|
||||
if ($revisionsToDelete->count() > 0) {
|
||||
$this->entityProvider->pageRevision->whereIn('id', $revisionsToDelete->pluck('id'))->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a page's html to be tagged correctly
|
||||
* within the system.
|
||||
* @param string $htmlText
|
||||
* @return string
|
||||
*/
|
||||
protected function formatHtml(string $htmlText)
|
||||
{
|
||||
if ($htmlText == '') {
|
||||
return $htmlText;
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
|
||||
|
||||
$container = $doc->documentElement;
|
||||
$body = $container->childNodes->item(0);
|
||||
$childNodes = $body->childNodes;
|
||||
|
||||
// Ensure no duplicate ids are used
|
||||
$idArray = [];
|
||||
|
||||
foreach ($childNodes as $index => $childNode) {
|
||||
/** @var \DOMElement $childNode */
|
||||
if (get_class($childNode) !== 'DOMElement') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Overwrite id if not a BookStack custom id
|
||||
if ($childNode->hasAttribute('id')) {
|
||||
$id = $childNode->getAttribute('id');
|
||||
if (strpos($id, 'bkmrk') === 0 && array_search($id, $idArray) === false) {
|
||||
$idArray[] = $id;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
|
||||
// Create an unique id for the element
|
||||
// Uses the content as a basis to ensure output is the same every time
|
||||
// the same content is passed through.
|
||||
$contentId = 'bkmrk-' . substr(strtolower(preg_replace('/\s+/', '-', trim($childNode->nodeValue))), 0, 20);
|
||||
$newId = urlencode($contentId);
|
||||
$loopIndex = 0;
|
||||
while (in_array($newId, $idArray)) {
|
||||
$newId = urlencode($contentId . '-' . $loopIndex);
|
||||
$loopIndex++;
|
||||
}
|
||||
|
||||
$childNode->setAttribute('id', $newId);
|
||||
$idArray[] = $newId;
|
||||
}
|
||||
|
||||
// Generate inner html as a string
|
||||
$html = '';
|
||||
foreach ($childNodes as $childNode) {
|
||||
$html .= $doc->saveHTML($childNode);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plain text version of a page's content.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @return string
|
||||
*/
|
||||
public function pageToPlainText(Page $page)
|
||||
{
|
||||
$html = $this->renderPage($page);
|
||||
return strip_tags($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new draft page instance.
|
||||
* @param Book $book
|
||||
* @param Chapter|null $chapter
|
||||
* @return \BookStack\Entities\Page
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function getDraftPage(Book $book, Chapter $chapter = null)
|
||||
{
|
||||
$page = $this->entityProvider->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->entityProvider->page->find($page->id);
|
||||
$this->permissionService->buildJointPermissionsForEntity($page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a page update draft.
|
||||
* @param Page $page
|
||||
* @param array $data
|
||||
* @return PageRevision|Page
|
||||
*/
|
||||
public function updatePageDraft(Page $page, array $data = [])
|
||||
{
|
||||
// If the page itself is a draft simply update that
|
||||
if ($page->draft) {
|
||||
$page->fill($data);
|
||||
if (isset($data['html'])) {
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
}
|
||||
$page->save();
|
||||
return $page;
|
||||
}
|
||||
|
||||
// Otherwise save the data to a revision
|
||||
$userId = user()->id;
|
||||
$drafts = $this->userUpdatePageDraftsQuery($page, $userId)->get();
|
||||
|
||||
if ($drafts->count() > 0) {
|
||||
$draft = $drafts->first();
|
||||
} else {
|
||||
$draft = $this->entityProvider->pageRevision->newInstance();
|
||||
$draft->page_id = $page->id;
|
||||
$draft->slug = $page->slug;
|
||||
$draft->book_slug = $page->book->slug;
|
||||
$draft->created_by = $userId;
|
||||
$draft->type = 'update_draft';
|
||||
}
|
||||
|
||||
$draft->fill($data);
|
||||
if (setting('app-editor') !== 'markdown') {
|
||||
$draft->markdown = '';
|
||||
}
|
||||
|
||||
$draft->save();
|
||||
return $draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a draft page to make it a normal page.
|
||||
* Sets the slug and updates the content.
|
||||
* @param Page $draftPage
|
||||
* @param array $input
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function publishPageDraft(Page $draftPage, array $input)
|
||||
{
|
||||
$draftPage->fill($input);
|
||||
|
||||
// Save page tags if present
|
||||
if (isset($input['tags'])) {
|
||||
$this->tagRepo->saveTagsToEntity($draftPage, $input['tags']);
|
||||
}
|
||||
|
||||
$draftPage->slug = $this->findSuitableSlug('page', $draftPage->name, false, $draftPage->book->id);
|
||||
$draftPage->html = $this->formatHtml($input['html']);
|
||||
$draftPage->text = $this->pageToPlainText($draftPage);
|
||||
$draftPage->draft = false;
|
||||
$draftPage->revision_count = 1;
|
||||
|
||||
$draftPage->save();
|
||||
$this->savePageRevision($draftPage, trans('entities.pages_initial_revision'));
|
||||
$this->searchService->indexEntity($draftPage);
|
||||
return $draftPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base query for getting user update drafts.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return mixed
|
||||
*/
|
||||
protected function userUpdatePageDraftsQuery(Page $page, int $userId)
|
||||
{
|
||||
return $this->entityProvider->pageRevision->where('created_by', '=', $userId)
|
||||
->where('type', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest updated draft revision for a particular page and user.
|
||||
* @param Page $page
|
||||
* @param $userId
|
||||
* @return PageRevision|null
|
||||
*/
|
||||
public function getUserPageDraft(Page $page, int $userId)
|
||||
{
|
||||
return $this->userUpdatePageDraftsQuery($page, $userId)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification message that informs the user that they are editing a draft page.
|
||||
* @param PageRevision $draft
|
||||
* @return string
|
||||
*/
|
||||
public function getUserPageDraftMessage(PageRevision $draft)
|
||||
{
|
||||
$message = trans('entities.pages_editing_draft_notification', ['timeDiff' => $draft->updated_at->diffForHumans()]);
|
||||
if ($draft->page->updated_at->timestamp <= $draft->updated_at->timestamp) {
|
||||
return $message;
|
||||
}
|
||||
return $message . "\n" . trans('entities.pages_draft_edited_notification');
|
||||
}
|
||||
|
||||
/**
|
||||
* A query to check for active update drafts on a particular page.
|
||||
* @param Page $page
|
||||
* @param int $minRange
|
||||
* @return mixed
|
||||
*/
|
||||
protected function activePageEditingQuery(Page $page, int $minRange = null)
|
||||
{
|
||||
$query = $this->entityProvider->pageRevision->where('type', '=', 'update_draft')
|
||||
->where('page_id', '=', $page->id)
|
||||
->where('updated_at', '>', $page->updated_at)
|
||||
->where('created_by', '!=', user()->id)
|
||||
->with('createdBy');
|
||||
|
||||
if ($minRange !== null) {
|
||||
$query = $query->where('updated_at', '>=', Carbon::now()->subMinutes($minRange));
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a page is being actively editing.
|
||||
* Checks for edits since last page updated.
|
||||
* Passing in a minuted range will check for edits
|
||||
* within the last x minutes.
|
||||
* @param Page $page
|
||||
* @param int $minRange
|
||||
* @return bool
|
||||
*/
|
||||
public function isPageEditingActive(Page $page, int $minRange = null)
|
||||
{
|
||||
$draftSearch = $this->activePageEditingQuery($page, $minRange);
|
||||
return $draftSearch->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a notification message concerning the editing activity on a particular page.
|
||||
* @param Page $page
|
||||
* @param int $minRange
|
||||
* @return string
|
||||
*/
|
||||
public function getPageEditingActiveMessage(Page $page, int $minRange = null)
|
||||
{
|
||||
$pageDraftEdits = $this->activePageEditingQuery($page, $minRange)->get();
|
||||
|
||||
$userMessage = $pageDraftEdits->count() > 1 ? trans('entities.pages_draft_edit_active.start_a', ['count' => $pageDraftEdits->count()]): trans('entities.pages_draft_edit_active.start_b', ['userName' => $pageDraftEdits->first()->createdBy->name]);
|
||||
$timeMessage = $minRange === null ? trans('entities.pages_draft_edit_active.time_a') : trans('entities.pages_draft_edit_active.time_b', ['minCount'=>$minRange]);
|
||||
return trans('entities.pages_draft_edit_active.message', ['start' => $userMessage, 'time' => $timeMessage]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the headers on the page to get a navigation menu
|
||||
* @param string $pageContent
|
||||
* @return array
|
||||
*/
|
||||
public function getPageNav(string $pageContent)
|
||||
{
|
||||
if ($pageContent == '') {
|
||||
return [];
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadHTML(mb_convert_encoding($pageContent, 'HTML-ENTITIES', 'UTF-8'));
|
||||
$xPath = new DOMXPath($doc);
|
||||
$headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6");
|
||||
|
||||
if (is_null($headers)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tree = collect([]);
|
||||
foreach ($headers as $header) {
|
||||
$text = $header->nodeValue;
|
||||
$tree->push([
|
||||
'nodeName' => strtolower($header->nodeName),
|
||||
'level' => intval(str_replace('h', '', $header->nodeName)),
|
||||
'link' => '#' . $header->getAttribute('id'),
|
||||
'text' => strlen($text) > 30 ? substr($text, 0, 27) . '...' : $text
|
||||
]);
|
||||
}
|
||||
|
||||
// Normalise headers if only smaller headers have been used
|
||||
if (count($tree) > 0) {
|
||||
$minLevel = $tree->pluck('level')->min();
|
||||
$tree = $tree->map(function ($header) use ($minLevel) {
|
||||
$header['level'] -= ($minLevel - 2);
|
||||
return $header;
|
||||
});
|
||||
}
|
||||
return $tree->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a revision's content back into a page.
|
||||
* @param Page $page
|
||||
* @param Book $book
|
||||
* @param int $revisionId
|
||||
* @return Page
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function restorePageRevision(Page $page, Book $book, int $revisionId)
|
||||
{
|
||||
$page->revision_count++;
|
||||
$this->savePageRevision($page);
|
||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
$page->fill($revision->toArray());
|
||||
$page->slug = $this->findSuitableSlug('page', $page->name, $page->id, $book->id);
|
||||
$page->text = $this->pageToPlainText($page);
|
||||
$page->updated_by = user()->id;
|
||||
$page->save();
|
||||
$this->searchService->indexEntity($page);
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the page's parent to the given entity.
|
||||
* @param Page $page
|
||||
* @param Entity $parent
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function changePageParent(Page $page, Entity $parent)
|
||||
{
|
||||
$book = $parent->isA('book') ? $parent : $parent->book;
|
||||
$page->chapter_id = $parent->isA('chapter') ? $parent->id : 0;
|
||||
$page->save();
|
||||
if ($page->book->id !== $book->id) {
|
||||
$page = $this->changeBook('page', $book->id, $page);
|
||||
}
|
||||
$page->load('book');
|
||||
$this->permissionService->buildJointPermissionsForEntity($book);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of a page in a new location with a new name.
|
||||
* @param \BookStack\Entities\Page $page
|
||||
* @param \BookStack\Entities\Entity $newParent
|
||||
* @param string $newName
|
||||
* @return \BookStack\Entities\Page
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function copyPage(Page $page, Entity $newParent, string $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);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Exceptions\FileUploadException;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Uploads\Attachment;
|
||||
|
@ -3,7 +3,7 @@
|
||||
use Activity;
|
||||
use BookStack\Auth\UserRepo;
|
||||
use BookStack\Entities\Book;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\ExportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
@ -3,7 +3,7 @@
|
||||
use Activity;
|
||||
use BookStack\Auth\UserRepo;
|
||||
use BookStack\Entities\Bookshelf;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\ExportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
@ -18,7 +18,7 @@ class BookshelfController extends Controller
|
||||
|
||||
/**
|
||||
* BookController constructor.
|
||||
* @param \BookStack\Entities\EntityRepo $entityRepo
|
||||
* @param \BookStack\Entities\Repos\EntityRepo $entityRepo
|
||||
* @param UserRepo $userRepo
|
||||
* @param \BookStack\Entities\ExportService $exportService
|
||||
*/
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use Activity;
|
||||
use BookStack\Auth\UserRepo;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\ExportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use Activity;
|
||||
use BookStack\Actions\CommentRepo;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@ -13,7 +13,7 @@ class CommentController extends Controller
|
||||
|
||||
/**
|
||||
* CommentController constructor.
|
||||
* @param \BookStack\Entities\EntityRepo $entityRepo
|
||||
* @param \BookStack\Entities\Repos\EntityRepo $entityRepo
|
||||
* @param \BookStack\Actions\CommentRepo $commentRepo
|
||||
*/
|
||||
public function __construct(EntityRepo $entityRepo, CommentRepo $commentRepo)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use Activity;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use Illuminate\Http\Response;
|
||||
use Views;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Exceptions\ImageUploadException;
|
||||
use BookStack\Repos\PageRepo;
|
||||
use BookStack\Uploads\Image;
|
||||
@ -219,7 +219,7 @@ class ImageController extends Controller
|
||||
|
||||
/**
|
||||
* Show the usage of an image on pages.
|
||||
* @param EntityRepo $entityRepo
|
||||
* @param \BookStack\Entities\Repos\EntityRepo $entityRepo
|
||||
* @param $id
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
use Activity;
|
||||
use BookStack\Auth\UserRepo;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\ExportService;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use GatherContent\Htmldiff\Htmldiff;
|
||||
use Illuminate\Http\Request;
|
||||
@ -13,19 +14,19 @@ use Views;
|
||||
class PageController extends Controller
|
||||
{
|
||||
|
||||
protected $entityRepo;
|
||||
protected $pageRepo;
|
||||
protected $exportService;
|
||||
protected $userRepo;
|
||||
|
||||
/**
|
||||
* PageController constructor.
|
||||
* @param \BookStack\Entities\EntityRepo $entityRepo
|
||||
* @param \BookStack\Entities\Repos\PageRepo $pageRepo
|
||||
* @param \BookStack\Entities\ExportService $exportService
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(EntityRepo $entityRepo, ExportService $exportService, UserRepo $userRepo)
|
||||
public function __construct(PageRepo $pageRepo, ExportService $exportService, UserRepo $userRepo)
|
||||
{
|
||||
$this->entityRepo = $entityRepo;
|
||||
$this->pageRepo = $pageRepo;
|
||||
$this->exportService = $exportService;
|
||||
$this->userRepo = $userRepo;
|
||||
parent::__construct();
|
||||
@ -42,11 +43,11 @@ class PageController extends Controller
|
||||
public function create($bookSlug, $chapterSlug = null)
|
||||
{
|
||||
if ($chapterSlug !== null) {
|
||||
$chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
||||
$chapter = $this->pageRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
||||
$book = $chapter->book;
|
||||
} else {
|
||||
$chapter = null;
|
||||
$book = $this->entityRepo->getBySlug('book', $bookSlug);
|
||||
$book = $this->pageRepo->getBySlug('book', $bookSlug);
|
||||
}
|
||||
|
||||
$parent = $chapter ? $chapter : $book;
|
||||
@ -54,7 +55,7 @@ class PageController extends Controller
|
||||
|
||||
// Redirect to draft edit screen if signed in
|
||||
if ($this->signedIn) {
|
||||
$draft = $this->entityRepo->getDraftPage($book, $chapter);
|
||||
$draft = $this->pageRepo->getDraftPage($book, $chapter);
|
||||
return redirect($draft->getUrl());
|
||||
}
|
||||
|
||||
@ -78,18 +79,18 @@ class PageController extends Controller
|
||||
]);
|
||||
|
||||
if ($chapterSlug !== null) {
|
||||
$chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
||||
$chapter = $this->pageRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
|
||||
$book = $chapter->book;
|
||||
} else {
|
||||
$chapter = null;
|
||||
$book = $this->entityRepo->getBySlug('book', $bookSlug);
|
||||
$book = $this->pageRepo->getBySlug('book', $bookSlug);
|
||||
}
|
||||
|
||||
$parent = $chapter ? $chapter : $book;
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
|
||||
$page = $this->entityRepo->getDraftPage($book, $chapter);
|
||||
$this->entityRepo->publishPageDraft($page, [
|
||||
$page = $this->pageRepo->getDraftPage($book, $chapter);
|
||||
$this->pageRepo->publishPageDraft($page, [
|
||||
'name' => $request->get('name'),
|
||||
'html' => ''
|
||||
]);
|
||||
@ -104,7 +105,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function editDraft($bookSlug, $pageId)
|
||||
{
|
||||
$draft = $this->entityRepo->getById('page', $pageId, true);
|
||||
$draft = $this->pageRepo->getById('page', $pageId, true);
|
||||
$this->checkOwnablePermission('page-create', $draft->parent);
|
||||
$this->setPageTitle(trans('entities.pages_edit_draft'));
|
||||
|
||||
@ -131,19 +132,19 @@ class PageController extends Controller
|
||||
]);
|
||||
|
||||
$input = $request->all();
|
||||
$draftPage = $this->entityRepo->getById('page', $pageId, true);
|
||||
$draftPage = $this->pageRepo->getById('page', $pageId, true);
|
||||
$book = $draftPage->book;
|
||||
|
||||
$parent = $draftPage->parent;
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
|
||||
if ($parent->isA('chapter')) {
|
||||
$input['priority'] = $this->entityRepo->getNewChapterPriority($parent);
|
||||
$input['priority'] = $this->pageRepo->getNewChapterPriority($parent);
|
||||
} else {
|
||||
$input['priority'] = $this->entityRepo->getNewBookPriority($parent);
|
||||
$input['priority'] = $this->pageRepo->getNewBookPriority($parent);
|
||||
}
|
||||
|
||||
$page = $this->entityRepo->publishPageDraft($draftPage, $input);
|
||||
$page = $this->pageRepo->publishPageDraft($draftPage, $input);
|
||||
|
||||
Activity::add($page, 'page_create', $book->id);
|
||||
return redirect($page->getUrl());
|
||||
@ -160,9 +161,9 @@ class PageController extends Controller
|
||||
public function show($bookSlug, $pageSlug)
|
||||
{
|
||||
try {
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
} catch (NotFoundException $e) {
|
||||
$page = $this->entityRepo->getPageByOldSlug($pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageByOldSlug($pageSlug, $bookSlug);
|
||||
if ($page === null) {
|
||||
throw $e;
|
||||
}
|
||||
@ -171,9 +172,9 @@ class PageController extends Controller
|
||||
|
||||
$this->checkOwnablePermission('page-view', $page);
|
||||
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$sidebarTree = $this->entityRepo->getBookChildren($page->book);
|
||||
$pageNav = $this->entityRepo->getPageNav($page->html);
|
||||
$page->html = $this->pageRepo->renderPage($page);
|
||||
$sidebarTree = $this->pageRepo->getBookChildren($page->book);
|
||||
$pageNav = $this->pageRepo->getPageNav($page->html);
|
||||
|
||||
// check if the comment's are enabled
|
||||
$commentsEnabled = !setting('app-disable-comments');
|
||||
@ -199,7 +200,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function getPageAjax($pageId)
|
||||
{
|
||||
$page = $this->entityRepo->getById('page', $pageId);
|
||||
$page = $this->pageRepo->getById('page', $pageId);
|
||||
return response()->json($page);
|
||||
}
|
||||
|
||||
@ -208,28 +209,29 @@ class PageController extends Controller
|
||||
* @param string $bookSlug
|
||||
* @param string $pageSlug
|
||||
* @return Response
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function edit($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->setPageTitle(trans('entities.pages_editing_named', ['pageName'=>$page->getShortName()]));
|
||||
$page->isDraft = false;
|
||||
|
||||
// Check for active editing
|
||||
$warnings = [];
|
||||
if ($this->entityRepo->isPageEditingActive($page, 60)) {
|
||||
$warnings[] = $this->entityRepo->getPageEditingActiveMessage($page, 60);
|
||||
if ($this->pageRepo->isPageEditingActive($page, 60)) {
|
||||
$warnings[] = $this->pageRepo->getPageEditingActiveMessage($page, 60);
|
||||
}
|
||||
|
||||
// Check for a current draft version for this user
|
||||
if ($this->entityRepo->hasUserGotPageDraft($page, $this->currentUser->id)) {
|
||||
$draft = $this->entityRepo->getUserPageDraft($page, $this->currentUser->id);
|
||||
$page->name = $draft->name;
|
||||
$page->html = $draft->html;
|
||||
$page->markdown = $draft->markdown;
|
||||
$userPageDraft = $this->pageRepo->getUserPageDraft($page, $this->currentUser->id);
|
||||
if ($userPageDraft !== null) {
|
||||
$page->name = $userPageDraft->name;
|
||||
$page->html = $userPageDraft->html;
|
||||
$page->markdown = $userPageDraft->markdown;
|
||||
$page->isDraft = true;
|
||||
$warnings [] = $this->entityRepo->getUserPageDraftMessage($draft);
|
||||
$warnings [] = $this->pageRepo->getUserPageDraftMessage($userPageDraft);
|
||||
}
|
||||
|
||||
if (count($warnings) > 0) {
|
||||
@ -257,9 +259,9 @@ class PageController extends Controller
|
||||
$this->validate($request, [
|
||||
'name' => 'required|string|max:255'
|
||||
]);
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->entityRepo->updatePage($page, $page->book->id, $request->all());
|
||||
$this->pageRepo->updatePage($page, $page->book->id, $request->all());
|
||||
Activity::add($page, 'page_update', $page->book->id);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
@ -272,7 +274,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function saveDraft(Request $request, $pageId)
|
||||
{
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
$page = $this->pageRepo->getById('page', $pageId, true);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
|
||||
if (!$this->signedIn) {
|
||||
@ -282,7 +284,7 @@ class PageController extends Controller
|
||||
], 500);
|
||||
}
|
||||
|
||||
$draft = $this->entityRepo->updatePageDraft($page, $request->only(['name', 'html', 'markdown']));
|
||||
$draft = $this->pageRepo->updatePageDraft($page, $request->only(['name', 'html', 'markdown']));
|
||||
|
||||
$updateTime = $draft->updated_at->timestamp;
|
||||
return response()->json([
|
||||
@ -300,7 +302,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function redirectFromLink($pageId)
|
||||
{
|
||||
$page = $this->entityRepo->getById('page', $pageId);
|
||||
$page = $this->pageRepo->getById('page', $pageId);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
||||
@ -312,7 +314,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showDelete($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
$this->setPageTitle(trans('entities.pages_delete_named', ['pageName'=>$page->getShortName()]));
|
||||
return view('pages/delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
|
||||
@ -328,7 +330,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showDeleteDraft($bookSlug, $pageId)
|
||||
{
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
$page = $this->pageRepo->getById('page', $pageId, true);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName'=>$page->getShortName()]));
|
||||
return view('pages/delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
|
||||
@ -343,10 +345,10 @@ class PageController extends Controller
|
||||
*/
|
||||
public function destroy($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$book = $page->book;
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
$this->entityRepo->destroyPage($page);
|
||||
$this->pageRepo->destroyPage($page);
|
||||
|
||||
Activity::addMessage('page_delete', $book->id, $page->name);
|
||||
session()->flash('success', trans('entities.pages_delete_success'));
|
||||
@ -362,11 +364,11 @@ class PageController extends Controller
|
||||
*/
|
||||
public function destroyDraft($bookSlug, $pageId)
|
||||
{
|
||||
$page = $this->entityRepo->getById('page', $pageId, true);
|
||||
$page = $this->pageRepo->getById('page', $pageId, true);
|
||||
$book = $page->book;
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
session()->flash('success', trans('entities.pages_delete_draft_success'));
|
||||
$this->entityRepo->destroyPage($page);
|
||||
$this->pageRepo->destroyPage($page);
|
||||
return redirect($book->getUrl());
|
||||
}
|
||||
|
||||
@ -378,7 +380,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRevisions($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->setPageTitle(trans('entities.pages_revisions_named', ['pageName'=>$page->getShortName()]));
|
||||
return view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]);
|
||||
}
|
||||
@ -392,7 +394,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRevision($bookSlug, $pageSlug, $revisionId)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
if ($revision === null) {
|
||||
abort(404);
|
||||
@ -417,7 +419,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRevisionChanges($bookSlug, $pageSlug, $revisionId)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$revision = $page->revisions()->where('id', '=', $revisionId)->first();
|
||||
if ($revision === null) {
|
||||
abort(404);
|
||||
@ -447,9 +449,9 @@ class PageController extends Controller
|
||||
*/
|
||||
public function restoreRevision($bookSlug, $pageSlug, $revisionId)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
$page = $this->entityRepo->restorePageRevision($page, $page->book, $revisionId);
|
||||
$page = $this->pageRepo->restorePageRevision($page, $page->book, $revisionId);
|
||||
Activity::add($page, 'page_restore', $page->book->id);
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
@ -466,7 +468,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function destroyRevision($bookSlug, $pageSlug, $revId)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-delete', $page);
|
||||
|
||||
$revision = $page->revisions()->where('id', '=', $revId)->first();
|
||||
@ -497,8 +499,8 @@ class PageController extends Controller
|
||||
*/
|
||||
public function exportPdf($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$page->html = $this->pageRepo->renderPage($page);
|
||||
$pdfContent = $this->exportService->pageToPdf($page);
|
||||
return $this->downloadResponse($pdfContent, $pageSlug . '.pdf');
|
||||
}
|
||||
@ -511,8 +513,8 @@ class PageController extends Controller
|
||||
*/
|
||||
public function exportHtml($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page->html = $this->entityRepo->renderPage($page);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$page->html = $this->pageRepo->renderPage($page);
|
||||
$containedHtml = $this->exportService->pageToContainedHtml($page);
|
||||
return $this->downloadResponse($containedHtml, $pageSlug . '.html');
|
||||
}
|
||||
@ -525,7 +527,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function exportPlainText($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$pageText = $this->exportService->pageToPlainText($page);
|
||||
return $this->downloadResponse($pageText, $pageSlug . '.txt');
|
||||
}
|
||||
@ -536,7 +538,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRecentlyCreated()
|
||||
{
|
||||
$pages = $this->entityRepo->getRecentlyCreatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-created'));
|
||||
$pages = $this->pageRepo->getRecentlyCreatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-created'));
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => trans('entities.recently_created_pages'),
|
||||
'pages' => $pages
|
||||
@ -549,7 +551,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRecentlyUpdated()
|
||||
{
|
||||
$pages = $this->entityRepo->getRecentlyUpdatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-updated'));
|
||||
$pages = $this->pageRepo->getRecentlyUpdatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-updated'));
|
||||
return view('pages/detailed-listing', [
|
||||
'title' => trans('entities.recently_updated_pages'),
|
||||
'pages' => $pages
|
||||
@ -564,7 +566,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showRestrict($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $page);
|
||||
$roles = $this->userRepo->getRestrictableRoles();
|
||||
return view('pages/restrictions', [
|
||||
@ -582,7 +584,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showMove($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
return view('pages/move', [
|
||||
'book' => $page->book,
|
||||
@ -600,7 +602,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function move($bookSlug, $pageSlug, Request $request)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
|
||||
$entitySelection = $request->get('entity_selection', null);
|
||||
@ -614,7 +616,7 @@ class PageController extends Controller
|
||||
|
||||
|
||||
try {
|
||||
$parent = $this->entityRepo->getById($entityType, $entityId);
|
||||
$parent = $this->pageRepo->getById($entityType, $entityId);
|
||||
} catch (\Exception $e) {
|
||||
session()->flash(trans('entities.selected_book_chapter_not_found'));
|
||||
return redirect()->back();
|
||||
@ -622,7 +624,7 @@ class PageController extends Controller
|
||||
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
|
||||
$this->entityRepo->changePageParent($page, $parent);
|
||||
$this->pageRepo->changePageParent($page, $parent);
|
||||
Activity::add($page, 'page_move', $page->book->id);
|
||||
session()->flash('success', trans('entities.pages_move_success', ['parentName' => $parent->name]));
|
||||
|
||||
@ -638,7 +640,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function showCopy($bookSlug, $pageSlug)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
session()->flashInput(['name' => $page->name]);
|
||||
return view('pages/copy', [
|
||||
@ -657,7 +659,7 @@ class PageController extends Controller
|
||||
*/
|
||||
public function copy($bookSlug, $pageSlug, Request $request)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('page-update', $page);
|
||||
|
||||
$entitySelection = $request->get('entity_selection', null);
|
||||
@ -669,7 +671,7 @@ class PageController extends Controller
|
||||
$entityId = intval($stringExploded[1]);
|
||||
|
||||
try {
|
||||
$parent = $this->entityRepo->getById($entityType, $entityId);
|
||||
$parent = $this->pageRepo->getById($entityType, $entityId);
|
||||
} catch (\Exception $e) {
|
||||
session()->flash(trans('entities.selected_book_chapter_not_found'));
|
||||
return redirect()->back();
|
||||
@ -678,7 +680,7 @@ class PageController extends Controller
|
||||
|
||||
$this->checkOwnablePermission('page-create', $parent);
|
||||
|
||||
$pageCopy = $this->entityRepo->copyPage($page, $parent, $request->get('name', ''));
|
||||
$pageCopy = $this->pageRepo->copyPage($page, $parent, $request->get('name', ''));
|
||||
|
||||
Activity::add($pageCopy, 'page_create', $pageCopy->book->id);
|
||||
session()->flash('success', trans('entities.pages_copy_success'));
|
||||
@ -696,9 +698,9 @@ class PageController extends Controller
|
||||
*/
|
||||
public function restrict($bookSlug, $pageSlug, Request $request)
|
||||
{
|
||||
$page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug);
|
||||
$page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
|
||||
$this->checkOwnablePermission('restrictions-manage', $page);
|
||||
$this->entityRepo->updateEntityPermissionsFromRequest($request, $page);
|
||||
$this->pageRepo->updateEntityPermissionsFromRequest($request, $page);
|
||||
session()->flash('success', trans('entities.pages_permissions_success'));
|
||||
return redirect($page->getUrl());
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php namespace BookStack\Http\Controllers;
|
||||
|
||||
use BookStack\Actions\ViewService;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\SearchService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
@ -13,7 +13,7 @@ class SearchController extends Controller
|
||||
|
||||
/**
|
||||
* SearchController constructor.
|
||||
* @param EntityRepo $entityRepo
|
||||
* @param \BookStack\Entities\Repos\EntityRepo $entityRepo
|
||||
* @param ViewService $viewService
|
||||
* @param SearchService $searchService
|
||||
*/
|
||||
|
@ -92,10 +92,15 @@ function baseUrl($path, $forceAppDomain = false)
|
||||
if ($isFullUrl && !$forceAppDomain) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$path = trim($path, '/');
|
||||
$trimBase = rtrim(config('app.url'), '/');
|
||||
|
||||
// Remove non-specified domain if forced and we have a domain
|
||||
if ($isFullUrl && $forceAppDomain) {
|
||||
if (strpos($path, $trimBase) === 0) {
|
||||
$path = trim(substr($path, strlen($trimBase) - 1));
|
||||
}
|
||||
$explodedPath = explode('/', $path);
|
||||
$path = implode('/', array_splice($explodedPath, 3));
|
||||
}
|
||||
@ -105,7 +110,7 @@ function baseUrl($path, $forceAppDomain = false)
|
||||
return url($path);
|
||||
}
|
||||
|
||||
return rtrim(config('app.url'), '/') . '/' . $path;
|
||||
return $trimBase . '/' . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@
|
||||
"php": ">=7.0.0",
|
||||
"ext-tidy": "*",
|
||||
"ext-dom": "*",
|
||||
"laravel/framework": "~5.5.42",
|
||||
"laravel/framework": "~5.5.44",
|
||||
"fideloper/proxy": "~3.3",
|
||||
"intervention/image": "^2.4",
|
||||
"laravel/socialite": "^3.0",
|
||||
|
811
composer.lock
generated
811
composer.lock
generated
File diff suppressed because it is too large
Load Diff
6971
package-lock.json
generated
6971
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
@ -10,34 +10,34 @@
|
||||
"permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0-beta.40",
|
||||
"@babel/polyfill": "^7.0.0-beta.40",
|
||||
"@babel/preset-env": "^7.0.0-beta.40",
|
||||
"autoprefixer": "^8.1.0",
|
||||
"babel-loader": "^8.0.0-beta.0",
|
||||
"css-loader": "^0.28.10",
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@babel/preset-env": "^7.1.0",
|
||||
"autoprefixer": "^8.6.5",
|
||||
"babel-loader": "^8.0.4",
|
||||
"css-loader": "^0.28.11",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"livereload": "^0.7.0",
|
||||
"node-sass": "^4.9.2",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"postcss-loader": "^2.1.1",
|
||||
"sass-loader": "^7.0.1",
|
||||
"node-sass": "^4.10.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"postcss-loader": "^2.1.6",
|
||||
"sass-loader": "^7.1.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"uglifyjs-webpack-plugin": "^1.2.3",
|
||||
"webpack": "^4.16.3",
|
||||
"webpack-cli": "^2.0.11"
|
||||
"uglifyjs-webpack-plugin": "^1.3.0",
|
||||
"webpack": "^4.24.0",
|
||||
"webpack-cli": "^2.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"clipboard": "^2.0.0",
|
||||
"codemirror": "^5.26.0",
|
||||
"dropzone": "^5.4.0",
|
||||
"clipboard": "^2.0.1",
|
||||
"codemirror": "^5.41.0",
|
||||
"dropzone": "^5.5.1",
|
||||
"jquery": "^3.3.1",
|
||||
"jquery-sortable": "^0.9.13",
|
||||
"markdown-it": "^8.3.1",
|
||||
"markdown-it-task-lists": "^2.0.0",
|
||||
"vue": "^2.2.6",
|
||||
"vuedraggable": "^2.14.1"
|
||||
"markdown-it": "^8.4.2",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"vue": "^2.5.17",
|
||||
"vuedraggable": "^2.16.0"
|
||||
},
|
||||
"browser": {
|
||||
"vue": "vue/dist/vue.common.js"
|
||||
|
@ -42,5 +42,6 @@
|
||||
<env name="GOOGLE_AUTO_REGISTER" value=""/>
|
||||
<env name="GOOGLE_AUTO_CONFIRM_EMAIL" value=""/>
|
||||
<env name="APP_URL" value="http://bookstack.dev"/>
|
||||
<env name="DEBUGBAR_ENABLED" value="false"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
use BookStack\Auth\Permissions\JointPermission;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Auth\User;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
|
||||
class CommandsTest extends TestCase
|
||||
{
|
||||
@ -53,10 +54,10 @@ class CommandsTest extends TestCase
|
||||
public function test_clear_revisions_command()
|
||||
{
|
||||
$this->asEditor();
|
||||
$entityRepo = $this->app[EntityRepo::class];
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$page = Page::first();
|
||||
$entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$entityRepo->updatePageDraft($page, ['name' => 'updated page', 'html' => '<p>new content in draft</p>', 'summary' => 'page revision testing']);
|
||||
$pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$pageRepo->updatePageDraft($page, ['name' => 'updated page', 'html' => '<p>new content in draft</p>', 'summary' => 'page revision testing']);
|
||||
|
||||
$this->assertDatabaseHas('page_revisions', [
|
||||
'page_id' => $page->id,
|
||||
|
@ -3,8 +3,9 @@
|
||||
use BookStack\Entities\Book;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Auth\UserRepo;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class EntityTest extends BrowserKitTest
|
||||
@ -193,7 +194,7 @@ class EntityTest extends BrowserKitTest
|
||||
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
||||
$this->actingAs($creator);
|
||||
app(UserRepo::class)->destroy($creator);
|
||||
app(EntityRepo::class)->savePageRevision($entities['page']);
|
||||
app(PageRepo::class)->savePageRevision($entities['page']);
|
||||
|
||||
$this->checkEntitiesViewable($entities);
|
||||
}
|
||||
@ -206,7 +207,7 @@ class EntityTest extends BrowserKitTest
|
||||
$entities = $this->createEntityChainBelongingToUser($creator, $updater);
|
||||
$this->actingAs($updater);
|
||||
app(UserRepo::class)->destroy($updater);
|
||||
app(EntityRepo::class)->savePageRevision($entities['page']);
|
||||
app(PageRepo::class)->savePageRevision($entities['page']);
|
||||
|
||||
$this->checkEntitiesViewable($entities);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?php namespace Tests;
|
||||
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
|
||||
class PageContentTest extends TestCase
|
||||
{
|
||||
@ -71,9 +72,9 @@ class PageContentTest extends TestCase
|
||||
{
|
||||
$this->asEditor();
|
||||
|
||||
$entityRepo = $this->app[EntityRepo::class];
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$page = Page::first();
|
||||
$entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$pageRevision = $page->revisions->last();
|
||||
|
||||
$revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
|
||||
@ -89,10 +90,10 @@ class PageContentTest extends TestCase
|
||||
{
|
||||
$this->asEditor();
|
||||
|
||||
$entityRepo = $this->app[EntityRepo::class];
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$page = Page::first();
|
||||
$entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'initial page revision testing']);
|
||||
$entityRepo->updatePage($page, $page->book_id, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'initial page revision testing']);
|
||||
$pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
|
||||
$page = Page::find($page->id);
|
||||
|
||||
|
||||
|
@ -1,16 +1,18 @@
|
||||
<?php namespace Tests;
|
||||
|
||||
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
|
||||
class PageDraftTest extends BrowserKitTest
|
||||
{
|
||||
protected $page;
|
||||
protected $entityRepo;
|
||||
protected $pageRepo;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->page = \BookStack\Entities\Page::first();
|
||||
$this->entityRepo = app('\BookStack\Entities\EntityRepo');
|
||||
$this->pageRepo = app(PageRepo::class);
|
||||
}
|
||||
|
||||
public function test_draft_content_shows_if_available()
|
||||
@ -20,7 +22,7 @@ class PageDraftTest extends BrowserKitTest
|
||||
->dontSeeInField('html', $addedContent);
|
||||
|
||||
$newContent = $this->page->html . $addedContent;
|
||||
$this->entityRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
$this->pageRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
$this->asAdmin()->visit($this->page->getUrl() . '/edit')
|
||||
->seeInField('html', $newContent);
|
||||
}
|
||||
@ -33,7 +35,7 @@ class PageDraftTest extends BrowserKitTest
|
||||
|
||||
$newContent = $this->page->html . $addedContent;
|
||||
$newUser = $this->getEditor();
|
||||
$this->entityRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
$this->pageRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
$this->actingAs($newUser)->visit($this->page->getUrl() . '/edit')
|
||||
->dontSeeInField('html', $newContent);
|
||||
}
|
||||
@ -41,7 +43,7 @@ class PageDraftTest extends BrowserKitTest
|
||||
public function test_alert_message_shows_if_editing_draft()
|
||||
{
|
||||
$this->asAdmin();
|
||||
$this->entityRepo->updatePageDraft($this->page, ['html' => 'test content']);
|
||||
$this->pageRepo->updatePageDraft($this->page, ['html' => 'test content']);
|
||||
$this->asAdmin()->visit($this->page->getUrl() . '/edit')
|
||||
->see('You are currently editing a draft');
|
||||
}
|
||||
@ -55,7 +57,7 @@ class PageDraftTest extends BrowserKitTest
|
||||
|
||||
$newContent = $this->page->html . $addedContent;
|
||||
$newUser = $this->getEditor();
|
||||
$this->entityRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
$this->pageRepo->updatePageDraft($this->page, ['html' => $newContent]);
|
||||
|
||||
$this->actingAs($newUser)
|
||||
->visit($this->page->getUrl() . '/edit')
|
||||
|
@ -3,7 +3,8 @@
|
||||
use BookStack\Entities\Book;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
|
||||
class SortTest extends TestCase
|
||||
{
|
||||
@ -18,8 +19,8 @@ class SortTest extends TestCase
|
||||
public function test_drafts_do_not_show_up()
|
||||
{
|
||||
$this->asAdmin();
|
||||
$entityRepo = app(EntityRepo::class);
|
||||
$draft = $entityRepo->getDraftPage($this->book);
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$draft = $pageRepo->getDraftPage($this->book);
|
||||
|
||||
$resp = $this->get($this->book->getUrl());
|
||||
$resp->assertSee($draft->name);
|
||||
|
19
tests/HelpersTest.php
Normal file
19
tests/HelpersTest.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php namespace Tests;
|
||||
|
||||
class HelpersTest extends TestCase
|
||||
{
|
||||
|
||||
public function test_base_url_takes_config_into_account()
|
||||
{
|
||||
config()->set('app.url', 'http://example.com/bookstack');
|
||||
$result = baseUrl('/');
|
||||
$this->assertEquals('http://example.com/bookstack/', $result);
|
||||
}
|
||||
|
||||
public function test_base_url_takes_extra_path_into_account_on_forced_domain()
|
||||
{
|
||||
config()->set('app.url', 'http://example.com/bookstack');
|
||||
$result = baseUrl('http://example.com/bookstack/', true);
|
||||
$this->assertEquals('http://example.com/bookstack/', $result);
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
<?php namespace Tests;
|
||||
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
use BookStack\Uploads\Image;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Uploads\ImageService;
|
||||
|
||||
class ImageTest extends TestCase
|
||||
@ -250,8 +251,8 @@ class ImageTest extends TestCase
|
||||
$upload->assertStatus(200);
|
||||
$image = Image::where('type', '=', 'gallery')->first();
|
||||
|
||||
$entityRepo = app(EntityRepo::class);
|
||||
$entityRepo->updatePage($page, $page->book_id, [
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$pageRepo->updatePage($page, $page->book_id, [
|
||||
'name' => $page->name,
|
||||
'html' => $page->html . "<img src=\"{$image->url}\">",
|
||||
'summary' => ''
|
||||
@ -263,7 +264,7 @@ class ImageTest extends TestCase
|
||||
$this->assertCount(0, $toDelete);
|
||||
|
||||
// Save a revision of our page without the image;
|
||||
$entityRepo->updatePage($page, $page->book_id, [
|
||||
$pageRepo->updatePage($page, $page->book_id, [
|
||||
'name' => $page->name,
|
||||
'html' => "<p>Hello</p>",
|
||||
'summary' => ''
|
||||
|
@ -5,7 +5,7 @@ use BookStack\Entities\Bookshelf;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Entity;
|
||||
use BookStack\Auth\User;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Entities\Page;
|
||||
|
||||
class RestrictionsTest extends BrowserKitTest
|
||||
|
@ -5,10 +5,11 @@ use BookStack\Entities\Bookshelf;
|
||||
use BookStack\Entities\Chapter;
|
||||
use BookStack\Entities\Entity;
|
||||
use BookStack\Entities\Page;
|
||||
use BookStack\Entities\EntityRepo;
|
||||
use BookStack\Entities\Repos\EntityRepo;
|
||||
use BookStack\Auth\Permissions\PermissionsRepo;
|
||||
use BookStack\Auth\Role;
|
||||
use BookStack\Auth\Permissions\PermissionService;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
use BookStack\Settings\SettingService;
|
||||
|
||||
trait SharedTestHelpers
|
||||
@ -78,7 +79,7 @@ trait SharedTestHelpers
|
||||
*/
|
||||
protected function regenEntityPermissions(Entity $entity)
|
||||
{
|
||||
$this->app[PermissionService::class]->buildJointPermissionsForEntity($entity);
|
||||
app(PermissionService::class)->buildJointPermissionsForEntity($entity);
|
||||
$entity->load('jointPermissions');
|
||||
}
|
||||
|
||||
@ -88,7 +89,7 @@ trait SharedTestHelpers
|
||||
* @return \BookStack\Entities\Bookshelf
|
||||
*/
|
||||
public function newShelf($input = ['name' => 'test shelf', 'description' => 'My new test shelf']) {
|
||||
return $this->app[EntityRepo::class]->createFromInput('bookshelf', $input, false);
|
||||
return app(EntityRepo::class)->createFromInput('bookshelf', $input, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,7 +98,7 @@ trait SharedTestHelpers
|
||||
* @return Book
|
||||
*/
|
||||
public function newBook($input = ['name' => 'test book', 'description' => 'My new test book']) {
|
||||
return $this->app[EntityRepo::class]->createFromInput('book', $input, false);
|
||||
return app(EntityRepo::class)->createFromInput('book', $input, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,7 +108,7 @@ trait SharedTestHelpers
|
||||
* @return \BookStack\Entities\Chapter
|
||||
*/
|
||||
public function newChapter($input = ['name' => 'test chapter', 'description' => 'My new test chapter'], Book $book) {
|
||||
return $this->app[EntityRepo::class]->createFromInput('chapter', $input, $book);
|
||||
return app(EntityRepo::class)->createFromInput('chapter', $input, $book);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,9 +118,9 @@ trait SharedTestHelpers
|
||||
*/
|
||||
public function newPage($input = ['name' => 'test page', 'html' => 'My new test page']) {
|
||||
$book = Book::first();
|
||||
$entityRepo = $this->app[EntityRepo::class];
|
||||
$draftPage = $entityRepo->getDraftPage($book);
|
||||
return $entityRepo->publishPageDraft($draftPage, $input);
|
||||
$pageRepo = app(PageRepo::class);
|
||||
$draftPage = $pageRepo->getDraftPage($book);
|
||||
return $pageRepo->publishPageDraft($draftPage, $input);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user