Showing
17 changed files
with
170 additions
and
97 deletions
| ... | @@ -158,13 +158,16 @@ class PageController extends Controller | ... | @@ -158,13 +158,16 @@ class PageController extends Controller |
| 158 | 158 | ||
| 159 | $this->checkOwnablePermission('page-view', $page); | 159 | $this->checkOwnablePermission('page-view', $page); |
| 160 | 160 | ||
| 161 | + $pageContent = $this->entityRepo->renderPage($page); | ||
| 161 | $sidebarTree = $this->entityRepo->getBookChildren($page->book); | 162 | $sidebarTree = $this->entityRepo->getBookChildren($page->book); |
| 162 | - $pageNav = $this->entityRepo->getPageNav($page); | 163 | + $pageNav = $this->entityRepo->getPageNav($pageContent); |
| 163 | 164 | ||
| 164 | Views::add($page); | 165 | Views::add($page); |
| 165 | $this->setPageTitle($page->getShortName()); | 166 | $this->setPageTitle($page->getShortName()); |
| 166 | - return view('pages/show', ['page' => $page, 'book' => $page->book, | 167 | + return view('pages/show', [ |
| 167 | - 'current' => $page, 'sidebarTree' => $sidebarTree, 'pageNav' => $pageNav]); | 168 | + 'page' => $page,'book' => $page->book, |
| 169 | + 'current' => $page, 'sidebarTree' => $sidebarTree, | ||
| 170 | + 'pageNav' => $pageNav, 'pageContent' => $pageContent]); | ||
| 168 | } | 171 | } |
| 169 | 172 | ||
| 170 | /** | 173 | /** |
| ... | @@ -430,6 +433,7 @@ class PageController extends Controller | ... | @@ -430,6 +433,7 @@ class PageController extends Controller |
| 430 | { | 433 | { |
| 431 | $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug); | 434 | $page = $this->entityRepo->getBySlug('page', $pageSlug, $bookSlug); |
| 432 | $pdfContent = $this->exportService->pageToPdf($page); | 435 | $pdfContent = $this->exportService->pageToPdf($page); |
| 436 | +// return $pdfContent; | ||
| 433 | return response()->make($pdfContent, 200, [ | 437 | return response()->make($pdfContent, 200, [ |
| 434 | 'Content-Type' => 'application/octet-stream', | 438 | 'Content-Type' => 'application/octet-stream', |
| 435 | 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf' | 439 | 'Content-Disposition' => 'attachment; filename="' . $pageSlug . '.pdf' | ... | ... |
| ... | @@ -139,7 +139,7 @@ class EntityRepo | ... | @@ -139,7 +139,7 @@ class EntityRepo |
| 139 | */ | 139 | */ |
| 140 | public function getById($type, $id, $allowDrafts = false) | 140 | public function getById($type, $id, $allowDrafts = false) |
| 141 | { | 141 | { |
| 142 | - return $this->entityQuery($type, $allowDrafts)->findOrFail($id); | 142 | + return $this->entityQuery($type, $allowDrafts)->find($id); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | /** | 145 | /** |
| ... | @@ -796,6 +796,52 @@ class EntityRepo | ... | @@ -796,6 +796,52 @@ class EntityRepo |
| 796 | return $html; | 796 | return $html; |
| 797 | } | 797 | } |
| 798 | 798 | ||
| 799 | + | ||
| 800 | + /** | ||
| 801 | + * Render the page for viewing, Parsing and performing features such as page transclusion. | ||
| 802 | + * @param Page $page | ||
| 803 | + * @return mixed|string | ||
| 804 | + */ | ||
| 805 | + public function renderPage(Page $page) | ||
| 806 | + { | ||
| 807 | + $content = $page->html; | ||
| 808 | + $matches = []; | ||
| 809 | + preg_match_all("/{{@\s?([0-9].*?)}}/", $content, $matches); | ||
| 810 | + if (count($matches[0]) === 0) return $content; | ||
| 811 | + | ||
| 812 | + foreach ($matches[1] as $index => $includeId) { | ||
| 813 | + $splitInclude = explode('#', $includeId, 2); | ||
| 814 | + $pageId = intval($splitInclude[0]); | ||
| 815 | + if (is_nan($pageId)) continue; | ||
| 816 | + | ||
| 817 | + $page = $this->getById('page', $pageId); | ||
| 818 | + if ($page === null) { | ||
| 819 | + $content = str_replace($matches[0][$index], '', $content); | ||
| 820 | + continue; | ||
| 821 | + } | ||
| 822 | + | ||
| 823 | + if (count($splitInclude) === 1) { | ||
| 824 | + $content = str_replace($matches[0][$index], $page->html, $content); | ||
| 825 | + continue; | ||
| 826 | + } | ||
| 827 | + | ||
| 828 | + $doc = new DOMDocument(); | ||
| 829 | + $doc->loadHTML(mb_convert_encoding('<body>'.$page->html.'</body>', 'HTML-ENTITIES', 'UTF-8')); | ||
| 830 | + $matchingElem = $doc->getElementById($splitInclude[1]); | ||
| 831 | + if ($matchingElem === null) { | ||
| 832 | + $content = str_replace($matches[0][$index], '', $content); | ||
| 833 | + continue; | ||
| 834 | + } | ||
| 835 | + $innerContent = ''; | ||
| 836 | + foreach ($matchingElem->childNodes as $childNode) { | ||
| 837 | + $innerContent .= $doc->saveHTML($childNode); | ||
| 838 | + } | ||
| 839 | + $content = str_replace($matches[0][$index], trim($innerContent), $content); | ||
| 840 | + } | ||
| 841 | + | ||
| 842 | + return $content; | ||
| 843 | + } | ||
| 844 | + | ||
| 799 | /** | 845 | /** |
| 800 | * Get a new draft page instance. | 846 | * Get a new draft page instance. |
| 801 | * @param Book $book | 847 | * @param Book $book |
| ... | @@ -835,15 +881,15 @@ class EntityRepo | ... | @@ -835,15 +881,15 @@ class EntityRepo |
| 835 | 881 | ||
| 836 | /** | 882 | /** |
| 837 | * Parse the headers on the page to get a navigation menu | 883 | * Parse the headers on the page to get a navigation menu |
| 838 | - * @param Page $page | 884 | + * @param String $pageContent |
| 839 | * @return array | 885 | * @return array |
| 840 | */ | 886 | */ |
| 841 | - public function getPageNav(Page $page) | 887 | + public function getPageNav($pageContent) |
| 842 | { | 888 | { |
| 843 | - if ($page->html == '') return []; | 889 | + if ($pageContent == '') return []; |
| 844 | libxml_use_internal_errors(true); | 890 | libxml_use_internal_errors(true); |
| 845 | $doc = new DOMDocument(); | 891 | $doc = new DOMDocument(); |
| 846 | - $doc->loadHTML(mb_convert_encoding($page->html, 'HTML-ENTITIES', 'UTF-8')); | 892 | + $doc->loadHTML(mb_convert_encoding($pageContent, 'HTML-ENTITIES', 'UTF-8')); |
| 847 | $xPath = new DOMXPath($doc); | 893 | $xPath = new DOMXPath($doc); |
| 848 | $headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6"); | 894 | $headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6"); |
| 849 | 895 | ... | ... |
| 1 | <?php namespace BookStack\Services; | 1 | <?php namespace BookStack\Services; |
| 2 | 2 | ||
| 3 | use BookStack\Page; | 3 | use BookStack\Page; |
| 4 | +use BookStack\Repos\EntityRepo; | ||
| 4 | 5 | ||
| 5 | class ExportService | 6 | class ExportService |
| 6 | { | 7 | { |
| 7 | 8 | ||
| 9 | + protected $entityRepo; | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * ExportService constructor. | ||
| 13 | + * @param $entityRepo | ||
| 14 | + */ | ||
| 15 | + public function __construct(EntityRepo $entityRepo) | ||
| 16 | + { | ||
| 17 | + $this->entityRepo = $entityRepo; | ||
| 18 | + } | ||
| 19 | + | ||
| 8 | /** | 20 | /** |
| 9 | * Convert a page to a self-contained HTML file. | 21 | * Convert a page to a self-contained HTML file. |
| 10 | * Includes required CSS & image content. Images are base64 encoded into the HTML. | 22 | * Includes required CSS & image content. Images are base64 encoded into the HTML. |
| ... | @@ -14,7 +26,7 @@ class ExportService | ... | @@ -14,7 +26,7 @@ class ExportService |
| 14 | public function pageToContainedHtml(Page $page) | 26 | public function pageToContainedHtml(Page $page) |
| 15 | { | 27 | { |
| 16 | $cssContent = file_get_contents(public_path('/css/export-styles.css')); | 28 | $cssContent = file_get_contents(public_path('/css/export-styles.css')); |
| 17 | - $pageHtml = view('pages/export', ['page' => $page, 'css' => $cssContent])->render(); | 29 | + $pageHtml = view('pages/export', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render(); |
| 18 | return $this->containHtml($pageHtml); | 30 | return $this->containHtml($pageHtml); |
| 19 | } | 31 | } |
| 20 | 32 | ||
| ... | @@ -26,7 +38,8 @@ class ExportService | ... | @@ -26,7 +38,8 @@ class ExportService |
| 26 | public function pageToPdf(Page $page) | 38 | public function pageToPdf(Page $page) |
| 27 | { | 39 | { |
| 28 | $cssContent = file_get_contents(public_path('/css/export-styles.css')); | 40 | $cssContent = file_get_contents(public_path('/css/export-styles.css')); |
| 29 | - $pageHtml = view('pages/pdf', ['page' => $page, 'css' => $cssContent])->render(); | 41 | + $pageHtml = view('pages/pdf', ['page' => $page, 'pageContent' => $this->entityRepo->renderPage($page), 'css' => $cssContent])->render(); |
| 42 | +// return $pageHtml; | ||
| 30 | $useWKHTML = config('snappy.pdf.binary') !== false; | 43 | $useWKHTML = config('snappy.pdf.binary') !== false; |
| 31 | $containedHtml = $this->containHtml($pageHtml); | 44 | $containedHtml = $this->containHtml($pageHtml); |
| 32 | if ($useWKHTML) { | 45 | if ($useWKHTML) { |
| ... | @@ -59,9 +72,13 @@ class ExportService | ... | @@ -59,9 +72,13 @@ class ExportService |
| 59 | $pathString = $srcString; | 72 | $pathString = $srcString; |
| 60 | } | 73 | } |
| 61 | if ($isLocal && !file_exists($pathString)) continue; | 74 | if ($isLocal && !file_exists($pathString)) continue; |
| 62 | - $imageContent = file_get_contents($pathString); | 75 | + try { |
| 63 | - $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent); | 76 | + $imageContent = file_get_contents($pathString); |
| 64 | - $newImageString = str_replace($srcString, $imageEncoded, $oldImgString); | 77 | + $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent); |
| 78 | + $newImageString = str_replace($srcString, $imageEncoded, $oldImgString); | ||
| 79 | + } catch (\ErrorException $e) { | ||
| 80 | + $newImageString = ''; | ||
| 81 | + } | ||
| 65 | $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent); | 82 | $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent); |
| 66 | } | 83 | } |
| 67 | } | 84 | } |
| ... | @@ -88,14 +105,14 @@ class ExportService | ... | @@ -88,14 +105,14 @@ class ExportService |
| 88 | 105 | ||
| 89 | /** | 106 | /** |
| 90 | * Converts the page contents into simple plain text. | 107 | * Converts the page contents into simple plain text. |
| 91 | - * This method filters any bad looking content to | 108 | + * This method filters any bad looking content to provide a nice final output. |
| 92 | - * provide a nice final output. | ||
| 93 | * @param Page $page | 109 | * @param Page $page |
| 94 | * @return mixed | 110 | * @return mixed |
| 95 | */ | 111 | */ |
| 96 | public function pageToPlainText(Page $page) | 112 | public function pageToPlainText(Page $page) |
| 97 | { | 113 | { |
| 98 | - $text = $page->text; | 114 | + $html = $this->entityRepo->renderPage($page); |
| 115 | + $text = strip_tags($html); | ||
| 99 | // Replace multiple spaces with single spaces | 116 | // Replace multiple spaces with single spaces |
| 100 | $text = preg_replace('/\ {2,}/', ' ', $text); | 117 | $text = preg_replace('/\ {2,}/', ' ', $text); |
| 101 | // Reduce multiple horrid whitespace characters. | 118 | // Reduce multiple horrid whitespace characters. | ... | ... |
| ... | @@ -157,7 +157,7 @@ class PermissionService | ... | @@ -157,7 +157,7 @@ class PermissionService |
| 157 | */ | 157 | */ |
| 158 | public function buildJointPermissionsForEntity(Entity $entity) | 158 | public function buildJointPermissionsForEntity(Entity $entity) |
| 159 | { | 159 | { |
| 160 | - $roles = $this->role->with('jointPermissions')->get(); | 160 | + $roles = $this->role->get(); |
| 161 | $entities = collect([$entity]); | 161 | $entities = collect([$entity]); |
| 162 | 162 | ||
| 163 | if ($entity->isA('book')) { | 163 | if ($entity->isA('book')) { |
| ... | @@ -177,7 +177,7 @@ class PermissionService | ... | @@ -177,7 +177,7 @@ class PermissionService |
| 177 | */ | 177 | */ |
| 178 | public function buildJointPermissionsForEntities(Collection $entities) | 178 | public function buildJointPermissionsForEntities(Collection $entities) |
| 179 | { | 179 | { |
| 180 | - $roles = $this->role->with('jointPermissions')->get(); | 180 | + $roles = $this->role->get(); |
| 181 | $this->deleteManyJointPermissionsForEntities($entities); | 181 | $this->deleteManyJointPermissionsForEntities($entities); |
| 182 | $this->createManyJointPermissions($entities, $roles); | 182 | $this->createManyJointPermissions($entities, $roles); |
| 183 | } | 183 | } |
| ... | @@ -564,6 +564,7 @@ class PermissionService | ... | @@ -564,6 +564,7 @@ class PermissionService |
| 564 | }); | 564 | }); |
| 565 | }); | 565 | }); |
| 566 | }); | 566 | }); |
| 567 | + $this->clean(); | ||
| 567 | return $q; | 568 | return $q; |
| 568 | } | 569 | } |
| 569 | 570 | ... | ... |
| ... | @@ -16,7 +16,9 @@ | ... | @@ -16,7 +16,9 @@ |
| 16 | "laravel-elixir": "^6.0.0-11", | 16 | "laravel-elixir": "^6.0.0-11", |
| 17 | "laravel-elixir-browserify-official": "^0.1.3", | 17 | "laravel-elixir-browserify-official": "^0.1.3", |
| 18 | "marked": "^0.3.5", | 18 | "marked": "^0.3.5", |
| 19 | - "moment": "^2.12.0", | 19 | + "moment": "^2.12.0" |
| 20 | - "zeroclipboard": "^2.2.0" | 20 | + }, |
| 21 | + "dependencies": { | ||
| 22 | + "clipboard": "^1.5.16" | ||
| 21 | } | 23 | } |
| 22 | } | 24 | } | ... | ... |
public/ZeroClipboard.swf
deleted
100644 → 0
No preview for this file type
| 1 | "use strict"; | 1 | "use strict"; |
| 2 | // Configure ZeroClipboard | 2 | // Configure ZeroClipboard |
| 3 | -import ZeroClipBoard from "zeroclipboard"; | 3 | +import Clipboard from "clipboard"; |
| 4 | 4 | ||
| 5 | export default window.setupPageShow = function (pageId) { | 5 | export default window.setupPageShow = function (pageId) { |
| 6 | 6 | ||
| 7 | // Set up pointer | 7 | // Set up pointer |
| 8 | let $pointer = $('#pointer').detach(); | 8 | let $pointer = $('#pointer').detach(); |
| 9 | + let pointerShowing = false; | ||
| 9 | let $pointerInner = $pointer.children('div.pointer').first(); | 10 | let $pointerInner = $pointer.children('div.pointer').first(); |
| 10 | let isSelection = false; | 11 | let isSelection = false; |
| 12 | + let pointerModeLink = true; | ||
| 13 | + let pointerSectionId = ''; | ||
| 11 | 14 | ||
| 12 | // Select all contents on input click | 15 | // Select all contents on input click |
| 13 | $pointer.on('click', 'input', function (e) { | 16 | $pointer.on('click', 'input', function (e) { |
| ... | @@ -15,19 +18,34 @@ export default window.setupPageShow = function (pageId) { | ... | @@ -15,19 +18,34 @@ export default window.setupPageShow = function (pageId) { |
| 15 | e.stopPropagation(); | 18 | e.stopPropagation(); |
| 16 | }); | 19 | }); |
| 17 | 20 | ||
| 18 | - // Set up copy-to-clipboard | 21 | + // Pointer mode toggle |
| 19 | - ZeroClipBoard.config({ | 22 | + $pointer.on('click', 'span.icon', event => { |
| 20 | - swfPath: window.baseUrl('/ZeroClipboard.swf') | 23 | + let $icon = $(event.currentTarget); |
| 24 | + pointerModeLink = !pointerModeLink; | ||
| 25 | + $icon.html(pointerModeLink ? '<i class="zmdi zmdi-link"></i>' : '<i class="zmdi zmdi-square-down"></i>'); | ||
| 26 | + updatePointerContent(); | ||
| 21 | }); | 27 | }); |
| 22 | - new ZeroClipBoard($pointer.find('button').first()[0]); | 28 | + |
| 29 | + // Set up clipboard | ||
| 30 | + let clipboard = new Clipboard('#pointer button'); | ||
| 23 | 31 | ||
| 24 | // Hide pointer when clicking away | 32 | // Hide pointer when clicking away |
| 25 | - $(document.body).find('*').on('click focus', function (e) { | 33 | + $(document.body).find('*').on('click focus', event => { |
| 26 | - if (!isSelection) { | 34 | + if (!pointerShowing || isSelection) return; |
| 27 | - $pointer.detach(); | 35 | + let target = $(event.target); |
| 28 | - } | 36 | + if (target.is('.zmdi') || $(event.target).closest('#pointer').length === 1) return; |
| 37 | + | ||
| 38 | + $pointer.detach(); | ||
| 39 | + pointerShowing = false; | ||
| 29 | }); | 40 | }); |
| 30 | 41 | ||
| 42 | + function updatePointerContent() { | ||
| 43 | + let inputText = pointerModeLink ? window.baseUrl(`/link/${pageId}#${pointerSectionId}`) : `{{@${pageId}#${pointerSectionId}}}`; | ||
| 44 | + if (pointerModeLink && inputText.indexOf('http') !== 0) inputText = window.location.protocol + "//" + window.location.host + inputText; | ||
| 45 | + | ||
| 46 | + $pointer.find('input').val(inputText); | ||
| 47 | + } | ||
| 48 | + | ||
| 31 | // Show pointer when selecting a single block of tagged content | 49 | // Show pointer when selecting a single block of tagged content |
| 32 | $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { | 50 | $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { |
| 33 | e.stopPropagation(); | 51 | e.stopPropagation(); |
| ... | @@ -36,12 +54,12 @@ export default window.setupPageShow = function (pageId) { | ... | @@ -36,12 +54,12 @@ export default window.setupPageShow = function (pageId) { |
| 36 | 54 | ||
| 37 | // Show pointer and set link | 55 | // Show pointer and set link |
| 38 | let $elem = $(this); | 56 | let $elem = $(this); |
| 39 | - let link = window.baseUrl('/link/' + pageId + '#' + $elem.attr('id')); | 57 | + pointerSectionId = $elem.attr('id'); |
| 40 | - if (link.indexOf('http') !== 0) link = window.location.protocol + "//" + window.location.host + link; | 58 | + updatePointerContent(); |
| 41 | - $pointer.find('input').val(link); | 59 | + |
| 42 | - $pointer.find('button').first().attr('data-clipboard-text', link); | ||
| 43 | $elem.before($pointer); | 60 | $elem.before($pointer); |
| 44 | $pointer.show(); | 61 | $pointer.show(); |
| 62 | + pointerShowing = true; | ||
| 45 | 63 | ||
| 46 | // Set pointer to sit near mouse-up position | 64 | // Set pointer to sit near mouse-up position |
| 47 | let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); | 65 | let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); | ... | ... |
| ... | @@ -136,9 +136,6 @@ | ... | @@ -136,9 +136,6 @@ |
| 136 | background-color: #EEE; | 136 | background-color: #EEE; |
| 137 | padding: $-s; | 137 | padding: $-s; |
| 138 | display: block; | 138 | display: block; |
| 139 | - > * { | ||
| 140 | - display: inline-block; | ||
| 141 | - } | ||
| 142 | &:before { | 139 | &:before { |
| 143 | font-family: 'Material-Design-Iconic-Font'; | 140 | font-family: 'Material-Design-Iconic-Font'; |
| 144 | padding-right: $-s; | 141 | padding-right: $-s; | ... | ... |
| ... | @@ -70,9 +70,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { | ... | @@ -70,9 +70,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { |
| 70 | #entity-selector-wrap .popup-body .form-group { | 70 | #entity-selector-wrap .popup-body .form-group { |
| 71 | margin: 0; | 71 | margin: 0; |
| 72 | } | 72 | } |
| 73 | -//body.ie #entity-selector-wrap .popup-body .form-group { | ||
| 74 | -// min-height: 60vh; | ||
| 75 | -//} | ||
| 76 | 73 | ||
| 77 | .image-manager-body { | 74 | .image-manager-body { |
| 78 | min-height: 70vh; | 75 | min-height: 70vh; | ... | ... |
| ... | @@ -138,6 +138,10 @@ | ... | @@ -138,6 +138,10 @@ |
| 138 | font-size: 18px; | 138 | font-size: 18px; |
| 139 | padding-top: 4px; | 139 | padding-top: 4px; |
| 140 | } | 140 | } |
| 141 | + span.icon { | ||
| 142 | + cursor: pointer; | ||
| 143 | + user-select: none; | ||
| 144 | + } | ||
| 141 | .button { | 145 | .button { |
| 142 | line-height: 1; | 146 | line-height: 1; |
| 143 | margin: 0 0 0 -4px; | 147 | margin: 0 0 0 -4px; | ... | ... |
| ... | @@ -7,6 +7,6 @@ | ... | @@ -7,6 +7,6 @@ |
| 7 | @if (isset($diff) && $diff) | 7 | @if (isset($diff) && $diff) |
| 8 | {!! $diff !!} | 8 | {!! $diff !!} |
| 9 | @else | 9 | @else |
| 10 | - {!! $page->html !!} | 10 | + {!! isset($pageContent) ? $pageContent : $page->html !!} |
| 11 | @endif | 11 | @endif |
| 12 | </div> | 12 | </div> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -53,9 +53,9 @@ | ... | @@ -53,9 +53,9 @@ |
| 53 | 53 | ||
| 54 | <div class="pointer-container" id="pointer"> | 54 | <div class="pointer-container" id="pointer"> |
| 55 | <div class="pointer anim"> | 55 | <div class="pointer anim"> |
| 56 | - <i class="zmdi zmdi-link"></i> | 56 | + <span class="icon text-primary"><i class="zmdi zmdi-link"></i></span> |
| 57 | - <input readonly="readonly" type="text" placeholder="url"> | 57 | + <input readonly="readonly" type="text" id="pointer-url" placeholder="url"> |
| 58 | - <button class="button icon" title="{{ trans('entities.pages_copy_link') }}" data-clipboard-text=""><i class="zmdi zmdi-copy"></i></button> | 58 | + <button class="button icon" data-clipboard-target="#pointer-url" type="button" title="{{ trans('entities.pages_copy_link') }}"><i class="zmdi zmdi-copy"></i></button> |
| 59 | </div> | 59 | </div> |
| 60 | </div> | 60 | </div> |
| 61 | 61 | ... | ... |
tests/Entity/PageContentTest.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +class PageContentTest extends TestCase | ||
| 4 | +{ | ||
| 5 | + | ||
| 6 | + public function test_page_includes() | ||
| 7 | + { | ||
| 8 | + $page = \BookStack\Page::first(); | ||
| 9 | + $secondPage = \BookStack\Page::all()->get(2); | ||
| 10 | + | ||
| 11 | + $secondPage->html = "<p id='section1'>Hello, This is a test</p><p id='section2'>This is a second block of content</p>"; | ||
| 12 | + $secondPage->save(); | ||
| 13 | + | ||
| 14 | + $this->asAdmin()->visit($page->getUrl()) | ||
| 15 | + ->dontSee('Hello, This is a test'); | ||
| 16 | + | ||
| 17 | + $originalHtml = $page->html; | ||
| 18 | + $page->html .= "{{@{$secondPage->id}}}"; | ||
| 19 | + $page->save(); | ||
| 20 | + | ||
| 21 | + $this->asAdmin()->visit($page->getUrl()) | ||
| 22 | + ->see('Hello, This is a test') | ||
| 23 | + ->see('This is a second block of content'); | ||
| 24 | + | ||
| 25 | + $page->html = $originalHtml . " Well {{@{$secondPage->id}#section2}}"; | ||
| 26 | + $page->save(); | ||
| 27 | + | ||
| 28 | + $this->asAdmin()->visit($page->getUrl()) | ||
| 29 | + ->dontSee('Hello, This is a test') | ||
| 30 | + ->see('Well This is a second block of content'); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | +} |
| ... | @@ -4,7 +4,7 @@ use BookStack\Tag; | ... | @@ -4,7 +4,7 @@ use BookStack\Tag; |
| 4 | use BookStack\Page; | 4 | use BookStack\Page; |
| 5 | use BookStack\Services\PermissionService; | 5 | use BookStack\Services\PermissionService; |
| 6 | 6 | ||
| 7 | -class TagTests extends \TestCase | 7 | +class TagTest extends \TestCase |
| 8 | { | 8 | { |
| 9 | 9 | ||
| 10 | protected $defaultTagCount = 20; | 10 | protected $defaultTagCount = 20; |
| ... | @@ -86,61 +86,16 @@ class TagTests extends \TestCase | ... | @@ -86,61 +86,16 @@ class TagTests extends \TestCase |
| 86 | $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'color'])); | 86 | $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'color'])); |
| 87 | $page = $this->getPageWithTags($attrs); | 87 | $page = $this->getPageWithTags($attrs); |
| 88 | 88 | ||
| 89 | - $this->asAdmin()->get('/ajax/tags/suggest?search=co')->seeJsonEquals(['color', 'country']); | 89 | + $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']); |
| 90 | - $this->asEditor()->get('/ajax/tags/suggest?search=co')->seeJsonEquals(['color', 'country']); | 90 | + $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']); |
| 91 | 91 | ||
| 92 | // Set restricted permission the page | 92 | // Set restricted permission the page |
| 93 | $page->restricted = true; | 93 | $page->restricted = true; |
| 94 | $page->save(); | 94 | $page->save(); |
| 95 | $permissionService->buildJointPermissionsForEntity($page); | 95 | $permissionService->buildJointPermissionsForEntity($page); |
| 96 | 96 | ||
| 97 | - $this->asAdmin()->get('/ajax/tags/suggest?search=co')->seeJsonEquals(['color', 'country']); | 97 | + $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']); |
| 98 | - $this->asEditor()->get('/ajax/tags/suggest?search=co')->seeJsonEquals([]); | 98 | + $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals([]); |
| 99 | - } | ||
| 100 | - | ||
| 101 | - public function test_entity_tag_updating() | ||
| 102 | - { | ||
| 103 | - $page = $this->getPageWithTags(); | ||
| 104 | - | ||
| 105 | - $testJsonData = [ | ||
| 106 | - ['name' => 'color', 'value' => 'red'], | ||
| 107 | - ['name' => 'color', 'value' => ' blue '], | ||
| 108 | - ['name' => 'city', 'value' => 'London '], | ||
| 109 | - ['name' => 'country', 'value' => ' England'], | ||
| 110 | - ]; | ||
| 111 | - $testResponseJsonData = [ | ||
| 112 | - ['name' => 'color', 'value' => 'red'], | ||
| 113 | - ['name' => 'color', 'value' => 'blue'], | ||
| 114 | - ['name' => 'city', 'value' => 'London'], | ||
| 115 | - ['name' => 'country', 'value' => 'England'], | ||
| 116 | - ]; | ||
| 117 | - | ||
| 118 | - // Do update request | ||
| 119 | - $this->asAdmin()->json("POST", "/ajax/tags/update/page/" . $page->id, ['tags' => $testJsonData]); | ||
| 120 | - $updateData = json_decode($this->response->getContent()); | ||
| 121 | - // Check data is correct | ||
| 122 | - $testDataCorrect = true; | ||
| 123 | - foreach ($updateData->tags as $data) { | ||
| 124 | - $testItem = ['name' => $data->name, 'value' => $data->value]; | ||
| 125 | - if (!in_array($testItem, $testResponseJsonData)) $testDataCorrect = false; | ||
| 126 | - } | ||
| 127 | - $testMessage = "Expected data was not found in the response.\nExpected Data: %s\nRecieved Data: %s"; | ||
| 128 | - $this->assertTrue($testDataCorrect, sprintf($testMessage, json_encode($testResponseJsonData), json_encode($updateData))); | ||
| 129 | - $this->assertTrue(isset($updateData->message), "No message returned in tag update response"); | ||
| 130 | - | ||
| 131 | - // Do get request | ||
| 132 | - $this->asAdmin()->get("/ajax/tags/get/page/" . $page->id); | ||
| 133 | - $getResponseData = json_decode($this->response->getContent()); | ||
| 134 | - // Check counts | ||
| 135 | - $this->assertTrue(count($getResponseData) === count($testJsonData), "The received tag count is incorrect"); | ||
| 136 | - // Check data is correct | ||
| 137 | - $testDataCorrect = true; | ||
| 138 | - foreach ($getResponseData as $data) { | ||
| 139 | - $testItem = ['name' => $data->name, 'value' => $data->value]; | ||
| 140 | - if (!in_array($testItem, $testResponseJsonData)) $testDataCorrect = false; | ||
| 141 | - } | ||
| 142 | - $testMessage = "Expected data was not found in the response.\nExpected Data: %s\nRecieved Data: %s"; | ||
| 143 | - $this->assertTrue($testDataCorrect, sprintf($testMessage, json_encode($testResponseJsonData), json_encode($getResponseData))); | ||
| 144 | } | 99 | } |
| 145 | 100 | ||
| 146 | } | 101 | } | ... | ... |
| ... | @@ -60,7 +60,7 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase | ... | @@ -60,7 +60,7 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase |
| 60 | */ | 60 | */ |
| 61 | public function asEditor() | 61 | public function asEditor() |
| 62 | { | 62 | { |
| 63 | - if($this->editor === null) { | 63 | + if ($this->editor === null) { |
| 64 | $this->editor = $this->getEditor(); | 64 | $this->editor = $this->getEditor(); |
| 65 | } | 65 | } |
| 66 | return $this->actingAs($this->editor); | 66 | return $this->actingAs($this->editor); | ... | ... |
-
Please register or sign in to post a comment