diff --git a/.env.example.complete b/.env.example.complete index 39e7b4360..45b1e1321 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -238,9 +238,9 @@ DISABLE_EXTERNAL_SERVICES=false # Example: AVATAR_URL=https://seccdn.libravatar.org/avatar/${hash}?s=${size}&d=identicon AVATAR_URL= -# Enable draw.io integration +# Enable diagrams.net integration # Can simply be true/false to enable/disable the integration. -# Alternatively, It can be URL to the draw.io instance you want to use. +# Alternatively, It can be URL to the diagrams.net instance you want to use. # For URLs, The following URL parameters should be included: embed=1&proto=json&spin=1 DRAWIO=true diff --git a/app/Entities/Managers/PageContent.php b/app/Entities/Managers/PageContent.php index e417b1caa..a787e5d99 100644 --- a/app/Entities/Managers/PageContent.php +++ b/app/Entities/Managers/PageContent.php @@ -2,7 +2,6 @@ use BookStack\Entities\Page; use DOMDocument; -use DOMElement; use DOMNodeList; use DOMXPath; @@ -44,18 +43,24 @@ class PageContent $container = $doc->documentElement; $body = $container->childNodes->item(0); $childNodes = $body->childNodes; + $xPath = new DOMXPath($doc); // Set ids on top-level nodes $idMap = []; foreach ($childNodes as $index => $childNode) { - $this->setUniqueId($childNode, $idMap); + [$oldId, $newId] = $this->setUniqueId($childNode, $idMap); + if ($newId && $newId !== $oldId) { + $this->updateLinks($xPath, '#' . $oldId, '#' . $newId); + } } // Ensure no duplicate ids within child items - $xPath = new DOMXPath($doc); $idElems = $xPath->query('//body//*//*[@id]'); foreach ($idElems as $domElem) { - $this->setUniqueId($domElem, $idMap); + [$oldId, $newId] = $this->setUniqueId($domElem, $idMap); + if ($newId && $newId !== $oldId) { + $this->updateLinks($xPath, '#' . $oldId, '#' . $newId); + } } // Generate inner html as a string @@ -67,23 +72,34 @@ class PageContent return $html; } + /** + * Update the all links to the $old location to instead point to $new. + */ + protected function updateLinks(DOMXPath $xpath, string $old, string $new) + { + $old = str_replace('"', '', $old); + $matchingLinks = $xpath->query('//body//*//*[@href="'.$old.'"]'); + foreach ($matchingLinks as $domElem) { + $domElem->setAttribute('href', $new); + } + } + /** * Set a unique id on the given DOMElement. * A map for existing ID's should be passed in to check for current existence. - * @param DOMElement $element - * @param array $idMap + * Returns a pair of strings in the format [old_id, new_id] */ - protected function setUniqueId($element, array &$idMap) + protected function setUniqueId(\DOMNode $element, array &$idMap): array { if (get_class($element) !== 'DOMElement') { - return; + return ['', '']; } - // Overwrite id if not a BookStack custom id + // Stop if there's an existing valid id that has not already been used. $existingId = $element->getAttribute('id'); if (strpos($existingId, 'bkmrk') === 0 && !isset($idMap[$existingId])) { $idMap[$existingId] = true; - return; + return [$existingId, $existingId]; } // Create an unique id for the element @@ -100,6 +116,7 @@ class PageContent $element->setAttribute('id', $newId); $idMap[$newId] = true; + return [$existingId, $newId]; } /** diff --git a/package-lock.json b/package-lock.json index cea03187c..06f13e8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -253,9 +253,9 @@ } }, "esbuild": { - "version": "0.6.30", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.6.30.tgz", - "integrity": "sha512-ZSZY461UPzTYYC3rqy1QiMtngk2WyXf+58MgC7tC22jkI90FXNgEl0hN3ipfn/UgZYzTW2GBcHiO7t0rSbHT7g==", + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.7.8.tgz", + "integrity": "sha512-6UT1nZB+8ja5avctUC6d3kGOUAhy6/ZYHljL4nk3++1ipadghBhUCAcwsTHsmUvdu04CcGKzo13mE+ZQ2O3zrA==", "dev": true }, "escape-string-regexp": { @@ -496,9 +496,9 @@ "dev": true }, "markdown-it": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.0.tgz", - "integrity": "sha512-+CvOnmbSubmQFSA9dKz1BRiaSMV7rhexl3sngKqFyXSagoA3fBdJQ8oZWtRy2knXdpDXaBw44euz37DeJQ9asg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz", + "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==", "requires": { "argparse": "^1.0.7", "entities": "~2.0.0", @@ -730,9 +730,9 @@ } }, "sass": { - "version": "1.26.10", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz", - "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==", + "version": "1.26.11", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.11.tgz", + "integrity": "sha512-W1l/+vjGjIamsJ6OnTe0K37U2DBO/dgsv2Z4c89XQ8ZOO6l/VwkqwLSqoYzJeJs6CLuGSTRWc91GbQFL3lvrvw==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -777,9 +777,9 @@ "dev": true }, "sortablejs": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", - "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.12.0.tgz", + "integrity": "sha512-bPn57rCjBRlt2sC24RBsu40wZsmLkSo2XeqG8k6DC1zru5eObQUIPPZAQG7W2SJ8FZQYq+BEJmvuw1Zxb3chqg==" }, "spdx-correct": { "version": "3.1.1", diff --git a/package.json b/package.json index 0c3c69a07..c3ca2add6 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "build:css:dev": "sass ./resources/sass:./public/dist", "build:css:watch": "sass ./resources/sass:./public/dist --watch", "build:css:production": "sass ./resources/sass:./public/dist -s compressed", - "build:js:dev": "esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2020", + "build:js:dev": "esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2019 --main-fields=module,main", "build:js:watch": "chokidar \"./resources/**/*.js\" -c \"npm run build:js:dev\"", - "build:js:production": "NODE_ENV=production esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --minify", + "build:js:production": "NODE_ENV=production esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2019 --main-fields=module,main --minify", "build": "npm-run-all --parallel build:*:dev", "production": "npm-run-all --parallel build:*:production", "dev": "npm-run-all --parallel watch livereload", @@ -16,18 +16,18 @@ }, "devDependencies": { "chokidar-cli": "^2.1.0", - "esbuild": "0.6.30", + "esbuild": "0.7.8", "livereload": "^0.9.1", "npm-run-all": "^4.1.5", "punycode": "^2.1.1", - "sass": "^1.26.10" + "sass": "^1.26.11" }, "dependencies": { "clipboard": "^2.0.6", "codemirror": "^5.58.1", "dropzone": "^5.7.2", - "markdown-it": "^11.0.0", + "markdown-it": "^11.0.1", "markdown-it-task-lists": "^2.1.1", - "sortablejs": "^1.10.2" + "sortablejs": "^1.12.0" } } diff --git a/public/.htaccess b/public/.htaccess index abe87b39d..3aec5e27e 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -11,9 +11,10 @@ # Redirect Trailing Slashes If Not A Folder... RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)/$ /$1 [L,R=301] + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] - # Handle Front Controller... + # Send Requests To Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] diff --git a/readme.md b/readme.md index 0c0626f5b..7c2872a0a 100644 --- a/readme.md +++ b/readme.md @@ -168,6 +168,6 @@ These are the great open-source projects used to help build BookStack: * [Snappy (WKHTML2PDF)](https://github.com/barryvdh/laravel-snappy) * [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper) * [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html) -* [Draw.io](https://github.com/jgraph/drawio) +* [diagrams.net](https://github.com/jgraph/drawio) * [Laravel Stats](https://github.com/stefanzweifel/laravel-stats) * [OneLogin's SAML PHP Toolkit](https://github.com/onelogin/php-saml) \ No newline at end of file diff --git a/resources/js/components/book-sort.js b/resources/js/components/book-sort.js index b0d64ad17..2b94ca4a7 100644 --- a/resources/js/components/book-sort.js +++ b/resources/js/components/book-sort.js @@ -1,4 +1,4 @@ -import {Sortable, MultiDrag} from "sortablejs"; +import Sortable from "sortablejs"; // Auto sort control const sortOperations = { @@ -43,7 +43,6 @@ class BookSort { this.input = elem.querySelector('[book-sort-input]'); const initialSortBox = elem.querySelector('.sort-box'); - Sortable.mount(new MultiDrag()); this.setupBookSortable(initialSortBox); this.setupSortPresets(); diff --git a/resources/js/services/http.js b/resources/js/services/http.js index 8ecd6c109..b05dd23bf 100644 --- a/resources/js/services/http.js +++ b/resources/js/services/http.js @@ -141,10 +141,14 @@ async function request(url, options = {}) { /** * Get the content from a fetch response. * Checks the content-type header to determine the format. - * @param response + * @param {Response} response * @returns {Promise} */ async function getResponseContent(response) { + if (response.status === 204) { + return null; + } + const responseContentType = response.headers.get('Content-Type'); const subType = responseContentType.split('/').pop(); diff --git a/resources/views/attachments/manager-edit-form.blade.php b/resources/views/attachments/manager-edit-form.blade.php index f3f11a0fc..ee86dc240 100644 --- a/resources/views/attachments/manager-edit-form.blade.php +++ b/resources/views/attachments/manager-edit-form.blade.php @@ -1,6 +1,7 @@
{{ trans('entities.attachments_edit_file') }}
diff --git a/resources/views/attachments/manager-link-form.blade.php b/resources/views/attachments/manager-link-form.blade.php index 6f22abb32..b51daa40e 100644 --- a/resources/views/attachments/manager-link-form.blade.php +++ b/resources/views/attachments/manager-link-form.blade.php @@ -4,6 +4,7 @@

{{ trans('entities.attachments_explain_link') }}

diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php index 4bfa97608..4628f7495 100644 --- a/resources/views/attachments/manager.blade.php +++ b/resources/views/attachments/manager.blade.php @@ -24,14 +24,14 @@ 'successMessage' => trans('entities.attachments_file_uploaded'), ])
-
-