Compare commits

...

14 Commits

Author SHA1 Message Date
Dan Brown
a9f5e98ba9 Drawings: Added class to extract drawio data from png files 2025-06-19 17:23:56 +01:00
Dan Brown
c4839c783a Updated translator & dependency attribution before release v25.05.1 2025-06-17 15:29:12 +01:00
Dan Brown
a5751a584c Updated translations with latest Crowdin changes (#5637) 2025-06-17 15:16:25 +01:00
Dan Brown
f518a3be37 Search: Updated indexer to handle non-breaking-spaces
Related to #5640
2025-06-17 14:00:13 +01:00
Dan Brown
0208f066c5 Comments: Fixed update notification text
For #5642
2025-06-17 13:42:25 +01:00
Dan Brown
2d0461b63a Merge pull request #5653 from BookStackApp/v25-05-1-lexical
Lexical Fixes for v25.05.1
2025-06-17 13:36:55 +01:00
Dan Brown
b913ae703d Lexical: Media form improvements
- Allowed re-editing of existing embed HTML code.
- Handled "src" form field when video is using child source tags.
2025-06-15 20:00:28 +01:00
Dan Brown
1611b0399f Lexical: Added a media toolbar, improved toolbars and media selection
- Updated toolbars to auto-refresh ui if it attempts to update targeting
  a DOM element which no longer exists.
- Removed MediaNode dom specific click handling which was causing
  selection issues, and did not seem to be needed now.
2025-06-15 15:22:27 +01:00
Dan Brown
8d4b8ff4f3 Lexical: Fixed media resize handling
- Updating height/width setting to clear any inline CSS width/height
  rules which would override and prevent resizes showing. This was
  common when switching media from old editor.
  Added test to cover.
- Updated resizer to track node so that it is retained & displayed
  across node DOM changes, which was previously causing the
  resizer/focus to disappear.
2025-06-15 13:55:42 +01:00
Dan Brown
77a88618c2 Lexical: Fixed double-bold text, updated tests
Double bold was due to text field exporting wrapping the output in <b>
tags when the main tag would already be strong.
2025-06-14 14:50:10 +01:00
Dan Brown
8b062d4795 Lexical: Fixed strange paragraph formatting behaviour
Formatting was not persisted on empty paragraphs, and was instead based
upon last format encountered in selection.
This was due to overly-hasty removal of other formatting code, which
this got caught it.
Restored required parts from prior codebase.

Also updated inline format button active indicator to reflect formats
using the above, so correct buttons are shown as active even when just
in an empty paragraph.
2025-06-13 19:40:13 +01:00
Dan Brown
717b516341 Lexical: Made table resize handles more efficent & less buggy
Fine mouse movement and handles will now only be active when actually
within a table, otherwise less frequent mouseovers are used to track if
in/out a table.
Hides handles when out of a table, preventing a range of issues with
stray handles floating about.
2025-06-13 16:38:53 +01:00
Dan Brown
fda242d3da Lexical: Fixed tiny image resizer on image insert
Added specific focus on image insert, and updated resize handler to
watch for load events and toggle a resize once loaded.
2025-06-13 15:58:59 +01:00
Dan Brown
aac547934c Deps: Bumped composer php package versions 2025-06-13 15:28:11 +01:00
55 changed files with 696 additions and 300 deletions

View File

@@ -487,3 +487,5 @@ jellium :: French
Qxlkdr :: Swedish Qxlkdr :: Swedish
Hari (muhhari) :: Indonesian Hari (muhhari) :: Indonesian
仙君御 (xjy) :: Chinese Simplified 仙君御 (xjy) :: Chinese Simplified
TapioM :: Finnish
lingb58 :: Chinese Traditional

View File

@@ -0,0 +1,7 @@
<?php
namespace BookStack\Exceptions;
class DrawioPngReaderException extends \Exception
{
}

View File

@@ -160,7 +160,9 @@ class SearchIndex
/** @var DOMNode $child */ /** @var DOMNode $child */
foreach ($doc->getBodyChildren() as $child) { foreach ($doc->getBodyChildren() as $child) {
$nodeName = $child->nodeName; $nodeName = $child->nodeName;
$termCounts = $this->textToTermCountMap(trim($child->textContent)); $text = trim($child->textContent);
$text = str_replace("\u{00A0}", ' ', $text);
$termCounts = $this->textToTermCountMap($text);
foreach ($termCounts as $term => $count) { foreach ($termCounts as $term => $count) {
$scoreChange = $count * ($elementScoreAdjustmentMap[$nodeName] ?? 1); $scoreChange = $count * ($elementScoreAdjustmentMap[$nodeName] ?? 1);
$scoresByTerm[$term] = ($scoresByTerm[$term] ?? 0) + $scoreChange; $scoresByTerm[$term] = ($scoresByTerm[$term] ?? 0) + $scoreChange;

View File

@@ -0,0 +1,122 @@
<?php
namespace BookStack\Uploads;
use BookStack\Exceptions\DrawioPngReaderException;
/**
* Reads the PNG file format: https://www.w3.org/TR/2003/REC-PNG-20031110/
* So that it can extract embedded drawing data for alternative use.
*/
class DrawioPngReader
{
/**
* @param resource $fileStream
*/
public function __construct(
protected $fileStream
) {
}
/**
* @throws DrawioPngReaderException
*/
public function extractDrawing(): string
{
$signature = fread($this->fileStream, 8);
$pngSignature = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
if ($signature !== $pngSignature) {
throw new DrawioPngReaderException('File does not appear to be a valid PNG file');
}
$offset = 8;
$searching = true;
while ($searching) {
fseek($this->fileStream, $offset);
$lengthBytes = $this->readData(4);
$chunkTypeBytes = $this->readData(4);
$length = unpack('Nvalue', $lengthBytes)['value'];
if ($chunkTypeBytes === 'tEXt') {
fseek($this->fileStream, $offset + 8);
$data = $this->readData($length);
$crc = $this->readData(4);
$drawingData = $this->readTextForDrawing($data);
if ($drawingData !== null) {
$crcResult = $this->calculateCrc($chunkTypeBytes . $data);
if ($crc !== $crcResult) {
throw new DrawioPngReaderException('Drawing data withing PNG file appears to be corrupted');
}
return $drawingData;
}
} else if ($chunkTypeBytes === 'IEND') {
$searching = false;
}
$offset += 12 + $length; // 12 = length + type + crc bytes
}
throw new DrawioPngReaderException('Unable to find drawing data within PNG file');
}
protected function readTextForDrawing(string $data): ?string
{
// Check the keyword is mxfile to ensure we're getting the right data
if (!str_starts_with($data, "mxfile\u{0}")) {
return null;
}
// Extract & cleanup the drawing text
$drawingText = substr($data, 7);
return urldecode($drawingText);
}
protected function readData(int $length): string
{
$bytes = fread($this->fileStream, $length);
if ($bytes === false || strlen($bytes) < $length) {
throw new DrawioPngReaderException('Unable to find drawing data within PNG file');
}
return $bytes;
}
protected function getCrcTable(): array
{
$table = [];
for ($n = 0; $n < 256; $n++) {
$c = $n;
for ($k = 0; $k < 8; $k++) {
if ($c & 1) {
$c = 0xedb88320 ^ ($c >> 1);
} else {
$c = $c >> 1;
}
}
$table[$n] = $c;
}
return $table;
}
/**
* Calculate a CRC for the given bytes following:
* https://www.w3.org/TR/2003/REC-PNG-20031110/#D-CRCAppendix
*/
protected function calculateCrc(string $bytes): string
{
$table = $this->getCrcTable();
$length = strlen($bytes);
$c = 0xffffffff;
for ($n = 0; $n < $length; $n++) {
$tableIndex = ($c ^ ord($bytes[$n])) & 0xff;
$c = $table[$tableIndex] ^ ($c >> 8);
}
return pack('N', $c ^ 0xffffffff);
}
}

194
composer.lock generated
View File

@@ -62,16 +62,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.343.22", "version": "3.344.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "174cc187df3bde52c21e9c00a4e99610a08732a3" "reference": "eb0bc621472592545539329499961a15a3f9f6dc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/174cc187df3bde52c21e9c00a4e99610a08732a3", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eb0bc621472592545539329499961a15a3f9f6dc",
"reference": "174cc187df3bde52c21e9c00a4e99610a08732a3", "reference": "eb0bc621472592545539329499961a15a3f9f6dc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -153,9 +153,9 @@
"support": { "support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions", "forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.343.22" "source": "https://github.com/aws/aws-sdk-php/tree/3.344.6"
}, },
"time": "2025-05-30T18:11:02+00:00" "time": "2025-06-12T18:03:59+00:00"
}, },
{ {
"name": "bacon/bacon-qr-code", "name": "bacon/bacon-qr-code",
@@ -1740,16 +1740,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v11.45.0", "version": "v11.45.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "d0730deb427632004d24801be7ca1ed2c10fbc4e" "reference": "b09ba32795b8e71df10856a2694706663984a239"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/d0730deb427632004d24801be7ca1ed2c10fbc4e", "url": "https://api.github.com/repos/laravel/framework/zipball/b09ba32795b8e71df10856a2694706663984a239",
"reference": "d0730deb427632004d24801be7ca1ed2c10fbc4e", "reference": "b09ba32795b8e71df10856a2694706663984a239",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1951,7 +1951,7 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
"time": "2025-05-20T15:15:58+00:00" "time": "2025-06-03T14:01:40+00:00"
}, },
{ {
"name": "laravel/prompts", "name": "laravel/prompts",
@@ -3285,16 +3285,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "3.9.1", "version": "3.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/CarbonPHP/carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "ced71f79398ece168e24f7f7710462f462310d4d" "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
"reference": "ced71f79398ece168e24f7f7710462f462310d4d", "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3302,9 +3302,9 @@
"ext-json": "*", "ext-json": "*",
"php": "^8.1", "php": "^8.1",
"psr/clock": "^1.0", "psr/clock": "^1.0",
"symfony/clock": "^6.3 || ^7.0", "symfony/clock": "^6.3.12 || ^7.0",
"symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-mbstring": "^1.0",
"symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
}, },
"provide": { "provide": {
"psr/clock-implementation": "1.0" "psr/clock-implementation": "1.0"
@@ -3312,14 +3312,13 @@
"require-dev": { "require-dev": {
"doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/dbal": "^3.6.3 || ^4.0",
"doctrine/orm": "^2.15.2 || ^3.0", "doctrine/orm": "^2.15.2 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.57.2", "friendsofphp/php-cs-fixer": "^3.75.0",
"kylekatarnls/multi-tester": "^2.5.3", "kylekatarnls/multi-tester": "^2.5.3",
"ondrejmirtes/better-reflection": "^6.25.0.4",
"phpmd/phpmd": "^2.15.0", "phpmd/phpmd": "^2.15.0",
"phpstan/extension-installer": "^1.3.1", "phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^1.11.2", "phpstan/phpstan": "^2.1.17",
"phpunit/phpunit": "^10.5.20", "phpunit/phpunit": "^10.5.46",
"squizlabs/php_codesniffer": "^3.9.0" "squizlabs/php_codesniffer": "^3.13.0"
}, },
"bin": [ "bin": [
"bin/carbon" "bin/carbon"
@@ -3387,7 +3386,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-05-01T19:51:51+00:00" "time": "2025-06-12T10:24:28+00:00"
}, },
{ {
"name": "nette/schema", "name": "nette/schema",
@@ -3453,16 +3452,16 @@
}, },
{ {
"name": "nette/utils", "name": "nette/utils",
"version": "v4.0.6", "version": "v4.0.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nette/utils.git", "url": "https://github.com/nette/utils.git",
"reference": "ce708655043c7050eb050df361c5e313cf708309" "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
"reference": "ce708655043c7050eb050df361c5e313cf708309", "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3533,9 +3532,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nette/utils/issues", "issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.0.6" "source": "https://github.com/nette/utils/tree/v4.0.7"
}, },
"time": "2025-03-30T21:06:30+00:00" "time": "2025-06-03T04:55:08+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
@@ -4775,20 +4774,20 @@
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "4.7.6", "version": "4.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088" "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088", "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
"ext-json": "*", "ext-json": "*",
"php": "^8.0", "php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0" "ramsey/collection": "^1.2 || ^2.0"
@@ -4797,26 +4796,23 @@
"rhumsaa/uuid": "self.version" "rhumsaa/uuid": "self.version"
}, },
"require-dev": { "require-dev": {
"captainhook/captainhook": "^5.10", "captainhook/captainhook": "^5.25",
"captainhook/plugin-composer": "^5.3", "captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"doctrine/annotations": "^1.8", "ergebnis/composer-normalize": "^2.47",
"ergebnis/composer-normalize": "^2.15", "mockery/mockery": "^1.6",
"mockery/mockery": "^1.3",
"paragonie/random-lib": "^2", "paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2", "php-mock/php-mock": "^2.6",
"php-mock/php-mock-mockery": "^1.3", "php-mock/php-mock-mockery": "^1.5",
"php-parallel-lint/php-parallel-lint": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.4.0",
"phpbench/phpbench": "^1.0", "phpbench/phpbench": "^1.2.14",
"phpstan/extension-installer": "^1.1", "phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^1.8", "phpstan/phpstan": "^2.1",
"phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^8.5 || ^9", "phpunit/phpunit": "^9.6",
"ramsey/composer-repl": "^1.4", "slevomat/coding-standard": "^8.18",
"slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.13"
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
}, },
"suggest": { "suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
@@ -4851,19 +4847,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/uuid/issues", "issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.6" "source": "https://github.com/ramsey/uuid/tree/4.8.1"
}, },
"funding": [ "time": "2025-06-01T06:28:46+00:00"
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
"type": "tidelift"
}
],
"time": "2024-04-27T21:32:50+00:00"
}, },
{ {
"name": "robrichards/xmlseclibs", "name": "robrichards/xmlseclibs",
@@ -7906,16 +7892,16 @@
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
"version": "2.18.0", "version": "2.18.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/filp/whoops.git", "url": "https://github.com/filp/whoops.git",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -7965,7 +7951,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/filp/whoops/issues", "issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.18.0" "source": "https://github.com/filp/whoops/tree/2.18.2"
}, },
"funding": [ "funding": [
{ {
@@ -7973,7 +7959,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-03-15T12:00:00+00:00" "time": "2025-06-11T20:42:19+00:00"
}, },
{ {
"name": "hamcrest/hamcrest-php", "name": "hamcrest/hamcrest-php",
@@ -8145,16 +8131,16 @@
}, },
{ {
"name": "larastan/larastan", "name": "larastan/larastan",
"version": "v3.4.0", "version": "v3.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/larastan/larastan.git", "url": "https://github.com/larastan/larastan.git",
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222" "reference": "36706736a0c51d3337478fab9c919d78d2e03404"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/1042fa0c2ee490bb6da7381f3323f7292ad68222", "url": "https://api.github.com/repos/larastan/larastan/zipball/36706736a0c51d3337478fab9c919d78d2e03404",
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222", "reference": "36706736a0c51d3337478fab9c919d78d2e03404",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -8222,7 +8208,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/larastan/larastan/issues", "issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.4.0" "source": "https://github.com/larastan/larastan/tree/v3.4.2"
}, },
"funding": [ "funding": [
{ {
@@ -8230,7 +8216,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-04-22T09:44:59+00:00" "time": "2025-06-10T09:34:58+00:00"
}, },
{ {
"name": "mockery/mockery", "name": "mockery/mockery",
@@ -8377,23 +8363,23 @@
}, },
{ {
"name": "nunomaduro/collision", "name": "nunomaduro/collision",
"version": "v8.8.0", "version": "v8.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nunomaduro/collision.git", "url": "https://github.com/nunomaduro/collision.git",
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8" "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8", "url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8", "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"filp/whoops": "^2.18.0", "filp/whoops": "^2.18.1",
"nunomaduro/termwind": "^2.3.0", "nunomaduro/termwind": "^2.3.1",
"php": "^8.2.0", "php": "^8.2.0",
"symfony/console": "^7.2.5" "symfony/console": "^7.3.0"
}, },
"conflict": { "conflict": {
"laravel/framework": "<11.44.2 || >=13.0.0", "laravel/framework": "<11.44.2 || >=13.0.0",
@@ -8401,15 +8387,15 @@
}, },
"require-dev": { "require-dev": {
"brianium/paratest": "^7.8.3", "brianium/paratest": "^7.8.3",
"larastan/larastan": "^3.2", "larastan/larastan": "^3.4.2",
"laravel/framework": "^11.44.2 || ^12.6", "laravel/framework": "^11.44.2 || ^12.18",
"laravel/pint": "^1.21.2", "laravel/pint": "^1.22.1",
"laravel/sail": "^1.41.0", "laravel/sail": "^1.43.1",
"laravel/sanctum": "^4.0.8", "laravel/sanctum": "^4.1.1",
"laravel/tinker": "^2.10.1", "laravel/tinker": "^2.10.1",
"orchestra/testbench-core": "^9.12.0 || ^10.1", "orchestra/testbench-core": "^9.12.0 || ^10.4",
"pestphp/pest": "^3.8.0", "pestphp/pest": "^3.8.2",
"sebastian/environment": "^7.2.0 || ^8.0" "sebastian/environment": "^7.2.1 || ^8.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@@ -8472,7 +8458,7 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2025-04-03T14:33:09+00:00" "time": "2025-06-11T01:04:21+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@@ -8975,16 +8961,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "11.5.21", "version": "11.5.23",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289" "reference": "86ebcd8a3dbcd1857d88505109b2a2b376501cde"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d565e2cdc21a7db9dc6c399c1fc2083b8010f289", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86ebcd8a3dbcd1857d88505109b2a2b376501cde",
"reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289", "reference": "86ebcd8a3dbcd1857d88505109b2a2b376501cde",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -9056,7 +9042,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.21" "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.23"
}, },
"funding": [ "funding": [
{ {
@@ -9080,7 +9066,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-05-21T12:35:00+00:00" "time": "2025-06-13T05:47:49+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -10022,16 +10008,16 @@
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
"version": "3.13.0", "version": "3.13.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "65ff2489553b83b4597e89c3b8b721487011d186" "reference": "1b71b4dd7e7ef651ac749cea67e513c0c832f4bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1b71b4dd7e7ef651ac749cea67e513c0c832f4bd",
"reference": "65ff2489553b83b4597e89c3b8b721487011d186", "reference": "1b71b4dd7e7ef651ac749cea67e513c0c832f4bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10102,7 +10088,7 @@
"type": "thanks_dev" "type": "thanks_dev"
} }
], ],
"time": "2025-05-11T03:36:00+00:00" "time": "2025-06-12T15:04:34+00:00"
}, },
{ {
"name": "ssddanbrown/asserthtml", "name": "ssddanbrown/asserthtml",

View File

@@ -486,7 +486,7 @@ Link: https://github.com/ramsey/collection.git
ramsey/uuid ramsey/uuid
License: MIT License: MIT
License File: vendor/ramsey/uuid/LICENSE License File: vendor/ramsey/uuid/LICENSE
Copyright: Copyright (c) 2012-2023 Ben Ramsey <***@*********.***> Copyright: Copyright (c) 2012-2025 Ben Ramsey <***@*********.***>
Source: https://github.com/ramsey/uuid.git Source: https://github.com/ramsey/uuid.git
Link: https://github.com/ramsey/uuid.git Link: https://github.com/ramsey/uuid.git
----------- -----------

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Vytvořit', 'create' => 'Vytvořit',
'update' => 'Aktualizovat', 'update' => 'Aktualizovat',
'edit' => 'Upravit', 'edit' => 'Upravit',
'archive' => 'Archive', 'archive' => 'Archivovat',
'unarchive' => 'Un-Archive', 'unarchive' => 'Od-Archivovat',
'sort' => 'Seřadit', 'sort' => 'Seřadit',
'move' => 'Přesunout', 'move' => 'Přesunout',
'copy' => 'Kopírovat', 'copy' => 'Kopírovat',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Stabilní obsah)', 'pages_edit_switch_to_markdown_stable' => '(Stabilní obsah)',
'pages_edit_switch_to_wysiwyg' => 'Přepnout na WYSIWYG Editor', 'pages_edit_switch_to_wysiwyg' => 'Přepnout na WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Přepnout na nový WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Přepnout na nový WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(V beta testování)',
'pages_edit_set_changelog' => 'Nastavit protokol změn', 'pages_edit_set_changelog' => 'Nastavit protokol změn',
'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli', 'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli',
'pages_edit_enter_changelog' => 'Zadejte protokol změn', 'pages_edit_enter_changelog' => 'Zadejte protokol změn',
@@ -392,11 +392,11 @@ return [
'comment' => 'Komentář', 'comment' => 'Komentář',
'comments' => 'Komentáře', 'comments' => 'Komentáře',
'comment_add' => 'Přidat komentář', 'comment_add' => 'Přidat komentář',
'comment_none' => 'No comments to display', 'comment_none' => 'Žádné komentáře k zobrazení',
'comment_placeholder' => 'Zde zadejte komentář', 'comment_placeholder' => 'Zde zadejte komentář',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count vlákno komentáře|:count vláken komentářů',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count archivováno',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Archivovaná vlákna',
'comment_save' => 'Uložit komentář', 'comment_save' => 'Uložit komentář',
'comment_new' => 'Nový komentář', 'comment_new' => 'Nový komentář',
'comment_created' => 'komentováno :createDiff', 'comment_created' => 'komentováno :createDiff',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Komentář odstraněn', 'comment_deleted_success' => 'Komentář odstraněn',
'comment_created_success' => 'Komentář přidán', 'comment_created_success' => 'Komentář přidán',
'comment_updated_success' => 'Komentář aktualizován', 'comment_updated_success' => 'Komentář aktualizován',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Komentář archivován',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Komentář od-archivován',
'comment_view' => 'View comment', 'comment_view' => 'Zobrazit komentář',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Přejít na vlákno',
'comment_delete_confirm' => 'Opravdu chcete odstranit tento komentář?', 'comment_delete_confirm' => 'Opravdu chcete odstranit tento komentář?',
'comment_in_reply_to' => 'Odpověď na :commentId', 'comment_in_reply_to' => 'Odpověď na :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Odkaz',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Zastaralý)',
'comment_editor_explain' => 'Zde jsou komentáře, které zůstaly na této stránce. Komentáře lze přidat a spravovat při prohlížení uložené stránky.', 'comment_editor_explain' => 'Zde jsou komentáře, které zůstaly na této stránce. Komentáře lze přidat a spravovat při prohlížení uložené stránky.',
// Revision // Revision

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Erstellen', 'create' => 'Erstellen',
'update' => 'Aktualisieren', 'update' => 'Aktualisieren',
'edit' => 'Bearbeiten', 'edit' => 'Bearbeiten',
'archive' => 'Archive', 'archive' => 'Archivieren',
'unarchive' => 'Un-Archive', 'unarchive' => 'Nicht mehr archivieren',
'sort' => 'Sortieren', 'sort' => 'Sortieren',
'move' => 'Verschieben', 'move' => 'Verschieben',
'copy' => 'Kopieren', 'copy' => 'Kopieren',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)', 'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)',
'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln', 'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln',
'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln', 'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(Im Beta-Test)',
'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen', 'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen',
'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer Änderungen ein', 'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer Änderungen ein',
'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben', 'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben',
@@ -392,11 +392,11 @@ return [
'comment' => 'Kommentar', 'comment' => 'Kommentar',
'comments' => 'Kommentare', 'comments' => 'Kommentare',
'comment_add' => 'Kommentieren', 'comment_add' => 'Kommentieren',
'comment_none' => 'No comments to display', 'comment_none' => 'Keine Kommentare vorhanden',
'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein', 'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count Thema|:count Themen',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count archiviert',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Archivierte Themen',
'comment_save' => 'Kommentar speichern', 'comment_save' => 'Kommentar speichern',
'comment_new' => 'Neuer Kommentar', 'comment_new' => 'Neuer Kommentar',
'comment_created' => ':createDiff kommentiert', 'comment_created' => ':createDiff kommentiert',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Kommentar gelöscht', 'comment_deleted_success' => 'Kommentar gelöscht',
'comment_created_success' => 'Kommentar hinzugefügt', 'comment_created_success' => 'Kommentar hinzugefügt',
'comment_updated_success' => 'Kommentar aktualisiert', 'comment_updated_success' => 'Kommentar aktualisiert',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Kommentar archiviert',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Kommentar nicht mehr archiviert',
'comment_view' => 'View comment', 'comment_view' => 'Kommentar ansehen',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Zum Thema springen',
'comment_delete_confirm' => 'Möchten Sie diesen Kommentar wirklich löschen?', 'comment_delete_confirm' => 'Möchten Sie diesen Kommentar wirklich löschen?',
'comment_in_reply_to' => 'Antwort auf :commentId', 'comment_in_reply_to' => 'Antwort auf :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Referenz',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Veraltet)',
'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.', 'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.',
// Revision // Revision

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Anlegen', 'create' => 'Anlegen',
'update' => 'Aktualisieren', 'update' => 'Aktualisieren',
'edit' => 'Bearbeiten', 'edit' => 'Bearbeiten',
'archive' => 'Archive', 'archive' => 'Archivieren',
'unarchive' => 'Un-Archive', 'unarchive' => 'Nicht mehr archivieren',
'sort' => 'Sortieren', 'sort' => 'Sortieren',
'move' => 'Verschieben', 'move' => 'Verschieben',
'copy' => 'Kopieren', 'copy' => 'Kopieren',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)', 'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)',
'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln', 'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln',
'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln', 'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(Im Beta-Test)',
'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen', 'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen',
'pages_edit_enter_changelog_desc' => 'Bitte gib eine kurze Zusammenfassung deiner Änderungen ein', 'pages_edit_enter_changelog_desc' => 'Bitte gib eine kurze Zusammenfassung deiner Änderungen ein',
'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben', 'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben',
@@ -392,11 +392,11 @@ return [
'comment' => 'Kommentar', 'comment' => 'Kommentar',
'comments' => 'Kommentare', 'comments' => 'Kommentare',
'comment_add' => 'Kommentieren', 'comment_add' => 'Kommentieren',
'comment_none' => 'No comments to display', 'comment_none' => 'Keine Kommentare vorhanden',
'comment_placeholder' => 'Gib hier deine Kommentare ein', 'comment_placeholder' => 'Gib hier deine Kommentare ein',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count Thema|:count Themen',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count archiviert',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Archivierte Themen',
'comment_save' => 'Kommentar speichern', 'comment_save' => 'Kommentar speichern',
'comment_new' => 'Neuer Kommentar', 'comment_new' => 'Neuer Kommentar',
'comment_created' => ':createDiff kommentiert', 'comment_created' => ':createDiff kommentiert',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Kommentar gelöscht', 'comment_deleted_success' => 'Kommentar gelöscht',
'comment_created_success' => 'Kommentar hinzugefügt', 'comment_created_success' => 'Kommentar hinzugefügt',
'comment_updated_success' => 'Kommentar aktualisiert', 'comment_updated_success' => 'Kommentar aktualisiert',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Kommentar archiviert',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Kommentar nicht mehr archiviert',
'comment_view' => 'View comment', 'comment_view' => 'Kommentar ansehen',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Zum Thema springen',
'comment_delete_confirm' => 'Möchtst du diesen Kommentar wirklich löschen?', 'comment_delete_confirm' => 'Möchtst du diesen Kommentar wirklich löschen?',
'comment_in_reply_to' => 'Antwort auf :commentId', 'comment_in_reply_to' => 'Antwort auf :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Referenz',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Veraltet)',
'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.', 'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.',
// Revision // Revision

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Crear', 'create' => 'Crear',
'update' => 'Actualizar', 'update' => 'Actualizar',
'edit' => 'Editar', 'edit' => 'Editar',
'archive' => 'Archive', 'archive' => 'Archivar',
'unarchive' => 'Un-Archive', 'unarchive' => 'Desarchivar',
'sort' => 'Ordenar', 'sort' => 'Ordenar',
'move' => 'Mover', 'move' => 'Mover',
'copy' => 'Copiar', 'copy' => 'Copiar',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)', 'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)',
'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG', 'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(En prueba beta)',
'pages_edit_set_changelog' => 'Ajustar Log de cambios', 'pages_edit_set_changelog' => 'Ajustar Log de cambios',
'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado', 'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado',
'pages_edit_enter_changelog' => 'Entrar al Log de cambios', 'pages_edit_enter_changelog' => 'Entrar al Log de cambios',
@@ -392,11 +392,11 @@ return [
'comment' => 'Comentario', 'comment' => 'Comentario',
'comments' => 'Comentarios', 'comments' => 'Comentarios',
'comment_add' => 'Añadir Comentario', 'comment_add' => 'Añadir Comentario',
'comment_none' => 'No comments to display', 'comment_none' => 'No hay comentarios para mostrar',
'comment_placeholder' => 'Introduzca su comentario aquí', 'comment_placeholder' => 'Introduzca su comentario aquí',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count hilo de comentarios|:count hilos de comentarios',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count Archivados',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Hilos archivados',
'comment_save' => 'Guardar comentario', 'comment_save' => 'Guardar comentario',
'comment_new' => 'Nuevo Comentario', 'comment_new' => 'Nuevo Comentario',
'comment_created' => 'comentado :createDiff', 'comment_created' => 'comentado :createDiff',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Comentario borrado', 'comment_deleted_success' => 'Comentario borrado',
'comment_created_success' => 'Comentario añadido', 'comment_created_success' => 'Comentario añadido',
'comment_updated_success' => 'Comentario actualizado', 'comment_updated_success' => 'Comentario actualizado',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Comentario archivado',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Comentario desarchivado',
'comment_view' => 'View comment', 'comment_view' => 'Ver comentario',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Ir al hilo',
'comment_delete_confirm' => '¿Está seguro de que quiere borrar este comentario?', 'comment_delete_confirm' => '¿Está seguro de que quiere borrar este comentario?',
'comment_in_reply_to' => 'En respuesta a :commentId', 'comment_in_reply_to' => 'En respuesta a :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Referencia',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(obsoleto)',
'comment_editor_explain' => 'Estos son los comentarios que se han escrito en esta página. Los comentarios se pueden añadir y administrar cuando se ve la página guardada.', 'comment_editor_explain' => 'Estos son los comentarios que se han escrito en esta página. Los comentarios se pueden añadir y administrar cuando se ve la página guardada.',
// Revision // Revision

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)', 'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)',
'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG', 'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(En prueba beta)',
'pages_edit_set_changelog' => 'Establecer cambios de registro', 'pages_edit_set_changelog' => 'Establecer cambios de registro',
'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado', 'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado',
'pages_edit_enter_changelog' => 'Entrar en cambio de registro', 'pages_edit_enter_changelog' => 'Entrar en cambio de registro',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Stabiilne sisu)', 'pages_edit_switch_to_markdown_stable' => '(Stabiilne sisu)',
'pages_edit_switch_to_wysiwyg' => 'Kasuta WYSIWYG redaktorit', 'pages_edit_switch_to_wysiwyg' => 'Kasuta WYSIWYG redaktorit',
'pages_edit_switch_to_new_wysiwyg' => 'Kasuta uut tekstiredaktorit', 'pages_edit_switch_to_new_wysiwyg' => 'Kasuta uut tekstiredaktorit',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(beetatestimisel)',
'pages_edit_set_changelog' => 'Muudatuste logi', 'pages_edit_set_changelog' => 'Muudatuste logi',
'pages_edit_enter_changelog_desc' => 'Sisesta tehtud muudatuste lühikirjeldus', 'pages_edit_enter_changelog_desc' => 'Sisesta tehtud muudatuste lühikirjeldus',
'pages_edit_enter_changelog' => 'Salvesta muudatuste logi', 'pages_edit_enter_changelog' => 'Salvesta muudatuste logi',

View File

@@ -10,7 +10,7 @@ return [
// Auth // Auth
'error_user_exists_different_creds' => 'Sähköpostiosoite :email on jo käytössä toisessa käyttäjätunnuksessa.', 'error_user_exists_different_creds' => 'Sähköpostiosoite :email on jo käytössä toisessa käyttäjätunnuksessa.',
'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'auth_pre_register_theme_prevention' => 'Käyttäjätiliä ei voitu rekisteröidä annetuille tiedoille',
'email_already_confirmed' => 'Sähköposti on jo vahvistettu, yritä kirjautua sisään.', 'email_already_confirmed' => 'Sähköposti on jo vahvistettu, yritä kirjautua sisään.',
'email_confirmation_invalid' => 'Tämä vahvistuslinkki ei ole voimassa tai sitä on jo käytetty, yritä rekisteröityä uudelleen.', 'email_confirmation_invalid' => 'Tämä vahvistuslinkki ei ole voimassa tai sitä on jo käytetty, yritä rekisteröityä uudelleen.',
'email_confirmation_expired' => 'Vahvistuslinkki on vanhentunut, uusi vahvistussähköposti on lähetetty.', 'email_confirmation_expired' => 'Vahvistuslinkki on vanhentunut, uusi vahvistussähköposti on lähetetty.',
@@ -38,7 +38,7 @@ Sovellus ei tunnista ulkoisen todennuspalvelun pyyntöä. Ongelman voi aiheuttaa
'social_driver_not_found' => 'Sosiaalisen median tilin ajuria ei löytynyt', 'social_driver_not_found' => 'Sosiaalisen median tilin ajuria ei löytynyt',
'social_driver_not_configured' => ':socialAccount-tilin asetuksia ei ole määritetty oikein.', 'social_driver_not_configured' => ':socialAccount-tilin asetuksia ei ole määritetty oikein.',
'invite_token_expired' => 'Tämä kutsulinkki on vanhentunut. Voit sen sijaan yrittää palauttaa tilisi salasanan.', 'invite_token_expired' => 'Tämä kutsulinkki on vanhentunut. Voit sen sijaan yrittää palauttaa tilisi salasanan.',
'login_user_not_found' => 'A user for this action could not be found.', 'login_user_not_found' => 'Käyttäjää tälle toiminnolle ei löytynyt.',
// System // System
'path_not_writable' => 'Tiedostopolkuun :filePath ei voitu ladata tiedostoa. Tarkista polun kirjoitusoikeudet.', 'path_not_writable' => 'Tiedostopolkuun :filePath ei voitu ladata tiedostoa. Tarkista polun kirjoitusoikeudet.',
@@ -79,7 +79,7 @@ Sovellus ei tunnista ulkoisen todennuspalvelun pyyntöä. Ongelman voi aiheuttaa
// Users // Users
'users_cannot_delete_only_admin' => 'Ainoaa ylläpitäjää ei voi poistaa', 'users_cannot_delete_only_admin' => 'Ainoaa ylläpitäjää ei voi poistaa',
'users_cannot_delete_guest' => 'Vieraskäyttäjää ei voi poistaa', 'users_cannot_delete_guest' => 'Vieraskäyttäjää ei voi poistaa',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send', 'users_could_not_send_invite' => 'Käyttäjää ei voitu luoda kutsun lähettämisen jälkeen',
// Roles // Roles
'role_cannot_be_edited' => 'Tätä roolia ei voi muokata', 'role_cannot_be_edited' => 'Tätä roolia ei voi muokata',
@@ -107,16 +107,16 @@ Sovellus ei tunnista ulkoisen todennuspalvelun pyyntöä. Ongelman voi aiheuttaa
'back_soon' => 'Se palautetaan pian.', 'back_soon' => 'Se palautetaan pian.',
// Import // Import
'import_zip_cant_read' => 'Could not read ZIP file.', 'import_zip_cant_read' => 'ZIP-tiedostoa ei voitu lukea.',
'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', 'import_zip_cant_decode_data' => 'ZIP-tiedoston data.json sisältöä ei löydy eikä sitä voitu purkaa.',
'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', 'import_zip_no_data' => 'ZIP-tiedostoilla ei ole odotettua kirjaa, lukua tai sivun sisältöä.',
'import_validation_failed' => 'Import ZIP failed to validate with errors:', 'import_validation_failed' => 'Tuonti ZIP epäonnistui virheiden kanssa:',
'import_zip_failed_notification' => 'Failed to import ZIP file.', 'import_zip_failed_notification' => 'ZIP-tiedoston tuominen epäonnistui.',
'import_perms_books' => 'You are lacking the required permissions to create books.', 'import_perms_books' => 'Sinulla ei ole tarvittavia oikeuksia luoda kirjoja.',
'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', 'import_perms_chapters' => 'Sinulla ei ole tarvittavia oikeuksia luoda kappaleita.',
'import_perms_pages' => 'You are lacking the required permissions to create pages.', 'import_perms_pages' => 'Sinulla ei ole tarvittavia oikeuksia luoda sivuja.',
'import_perms_images' => 'You are lacking the required permissions to create images.', 'import_perms_images' => 'Sinulla ei ole tarvittavia oikeuksia luoda kuvia.',
'import_perms_attachments' => 'You are lacking the required permission to create attachments.', 'import_perms_attachments' => 'Sinulla ei ole tarvittavaa lupaa luoda liitteitä.',
// API errors // API errors
'api_no_authorization_found' => 'Pyynnöstä ei löytynyt valtuutuskoodia', 'api_no_authorization_found' => 'Pyynnöstä ei löytynyt valtuutuskoodia',

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Crea', 'create' => 'Crea',
'update' => 'Aggiorna', 'update' => 'Aggiorna',
'edit' => 'Modifica', 'edit' => 'Modifica',
'archive' => 'Archive', 'archive' => 'Archivia',
'unarchive' => 'Un-Archive', 'unarchive' => 'Ripristina',
'sort' => 'Ordina', 'sort' => 'Ordina',
'move' => 'Sposta', 'move' => 'Sposta',
'copy' => 'Copia', 'copy' => 'Copia',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Contenuto stabile)', 'pages_edit_switch_to_markdown_stable' => '(Contenuto stabile)',
'pages_edit_switch_to_wysiwyg' => 'Passa all\'editor WYSIWYG', 'pages_edit_switch_to_wysiwyg' => 'Passa all\'editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Passa al nuovo WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Passa al nuovo WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Test)',
'pages_edit_set_changelog' => 'Imposta changelog', 'pages_edit_set_changelog' => 'Imposta changelog',
'pages_edit_enter_changelog_desc' => 'Inserisci una breve descrizione dei cambiamenti che hai apportato', 'pages_edit_enter_changelog_desc' => 'Inserisci una breve descrizione dei cambiamenti che hai apportato',
'pages_edit_enter_changelog' => 'Inserisci changelog', 'pages_edit_enter_changelog' => 'Inserisci changelog',
@@ -392,11 +392,11 @@ return [
'comment' => 'Commento', 'comment' => 'Commento',
'comments' => 'Commenti', 'comments' => 'Commenti',
'comment_add' => 'Aggiungi commento', 'comment_add' => 'Aggiungi commento',
'comment_none' => 'No comments to display', 'comment_none' => 'Nessun commento da visualizzare',
'comment_placeholder' => 'Scrivi un commento', 'comment_placeholder' => 'Scrivi un commento',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count Archiviato',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Discussioni Archiviate',
'comment_save' => 'Salva commento', 'comment_save' => 'Salva commento',
'comment_new' => 'Nuovo commento', 'comment_new' => 'Nuovo commento',
'comment_created' => 'ha commentato :createDiff', 'comment_created' => 'ha commentato :createDiff',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Commento eliminato', 'comment_deleted_success' => 'Commento eliminato',
'comment_created_success' => 'Commento aggiunto', 'comment_created_success' => 'Commento aggiunto',
'comment_updated_success' => 'Commento aggiornato', 'comment_updated_success' => 'Commento aggiornato',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Commento archiviato',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Commento ripristinato',
'comment_view' => 'View comment', 'comment_view' => 'Visualizza commento',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Vai al thread',
'comment_delete_confirm' => 'Sei sicuro di voler eliminare questo commento?', 'comment_delete_confirm' => 'Sei sicuro di voler eliminare questo commento?',
'comment_in_reply_to' => 'In risposta a :commentId', 'comment_in_reply_to' => 'In risposta a :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Riferimento',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Obsoleto)',
'comment_editor_explain' => 'Ecco i commenti che sono stati lasciati in questa pagina. I commenti possono essere aggiunti e gestiti quando si visualizza la pagina salvata.', 'comment_editor_explain' => 'Ecco i commenti che sono stati lasciati in questa pagina. I commenti possono essere aggiunti e gestiti quando si visualizza la pagina salvata.',
// Revision // Revision

View File

@@ -30,8 +30,8 @@ return [
'create' => '作成', 'create' => '作成',
'update' => '更新', 'update' => '更新',
'edit' => '編集', 'edit' => '編集',
'archive' => 'Archive', 'archive' => 'アーカイブ',
'unarchive' => 'Un-Archive', 'unarchive' => 'アーカイブ解除',
'sort' => '並び順', 'sort' => '並び順',
'move' => '移動', 'move' => '移動',
'copy' => 'コピー', 'copy' => 'コピー',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(安定したコンテンツ)', 'pages_edit_switch_to_markdown_stable' => '(安定したコンテンツ)',
'pages_edit_switch_to_wysiwyg' => 'WYSIWYGエディタに切り替え', 'pages_edit_switch_to_wysiwyg' => 'WYSIWYGエディタに切り替え',
'pages_edit_switch_to_new_wysiwyg' => '新しいWYSIWYGエディタに切り替える', 'pages_edit_switch_to_new_wysiwyg' => '新しいWYSIWYGエディタに切り替える',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(ベータテスト版)',
'pages_edit_set_changelog' => '編集内容についての説明', 'pages_edit_set_changelog' => '編集内容についての説明',
'pages_edit_enter_changelog_desc' => 'どのような変更を行ったのかを記録してください', 'pages_edit_enter_changelog_desc' => 'どのような変更を行ったのかを記録してください',
'pages_edit_enter_changelog' => '編集内容を入力', 'pages_edit_enter_changelog' => '編集内容を入力',
@@ -393,11 +393,11 @@ return [
'comment' => 'コメント', 'comment' => 'コメント',
'comments' => 'コメント', 'comments' => 'コメント',
'comment_add' => 'コメント追加', 'comment_add' => 'コメント追加',
'comment_none' => 'No comments to display', 'comment_none' => '表示するコメントがありません',
'comment_placeholder' => 'コメントを記入してください', 'comment_placeholder' => 'コメントを記入してください',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count 個のコメントスレッド|:count 個のコメントスレッド',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count 個のアーカイブ',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'アーカイブされたスレッド',
'comment_save' => 'コメントを保存', 'comment_save' => 'コメントを保存',
'comment_new' => '新規コメント作成', 'comment_new' => '新規コメント作成',
'comment_created' => 'コメントを作成しました :createDiff', 'comment_created' => 'コメントを作成しました :createDiff',
@@ -406,14 +406,14 @@ return [
'comment_deleted_success' => 'コメントを削除しました', 'comment_deleted_success' => 'コメントを削除しました',
'comment_created_success' => 'コメントを追加しました', 'comment_created_success' => 'コメントを追加しました',
'comment_updated_success' => 'コメントを更新しました', 'comment_updated_success' => 'コメントを更新しました',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'コメントをアーカイブしました',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'コメントのアーカイブを解除しました',
'comment_view' => 'View comment', 'comment_view' => 'コメントを表示',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'スレッドにジャンプ',
'comment_delete_confirm' => '本当にこのコメントを削除しますか?', 'comment_delete_confirm' => '本当にこのコメントを削除しますか?',
'comment_in_reply_to' => ':commentIdへ返信', 'comment_in_reply_to' => ':commentIdへ返信',
'comment_reference' => 'Reference', 'comment_reference' => '参照箇所',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(以前の記述)',
'comment_editor_explain' => 'ここにはページに付けられたコメントを表示します。 コメントの追加と管理は保存されたページの表示時に行うことができます。', 'comment_editor_explain' => 'ここにはページに付けられたコメントを表示します。 コメントの追加と管理は保存されたページの表示時に行うことができます。',
// Revision // Revision

View File

@@ -30,8 +30,8 @@ return [
'create' => 'Aanmaken', 'create' => 'Aanmaken',
'update' => 'Bijwerken', 'update' => 'Bijwerken',
'edit' => 'Bewerk', 'edit' => 'Bewerk',
'archive' => 'Archive', 'archive' => 'Archiveer',
'unarchive' => 'Un-Archive', 'unarchive' => 'Terughalen',
'sort' => 'Sorteer', 'sort' => 'Sorteer',
'move' => 'Verplaats', 'move' => 'Verplaats',
'copy' => 'Kopieer', 'copy' => 'Kopieer',

View File

@@ -23,7 +23,7 @@ return [
'meta_updated' => 'Bijgewerkt: :timeLength', 'meta_updated' => 'Bijgewerkt: :timeLength',
'meta_updated_name' => 'Bijgewerkt: :timeLength door :user', 'meta_updated_name' => 'Bijgewerkt: :timeLength door :user',
'meta_owned_name' => 'Eigendom van :user', 'meta_owned_name' => 'Eigendom van :user',
'meta_reference_count' => 'Gerefereerd door :count item|Gerefereerd door :count items', 'meta_reference_count' => 'Verwijzing in :count item|Verwijzing in :count items',
'entity_select' => 'Entiteit selecteren', 'entity_select' => 'Entiteit selecteren',
'entity_select_lack_permission' => 'Je hebt niet de vereiste machtiging om dit item te selecteren', 'entity_select_lack_permission' => 'Je hebt niet de vereiste machtiging om dit item te selecteren',
'images' => 'Afbeeldingen', 'images' => 'Afbeeldingen',
@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Stabiele Inhoud)', 'pages_edit_switch_to_markdown_stable' => '(Stabiele Inhoud)',
'pages_edit_switch_to_wysiwyg' => 'Schakel naar de WYSIWYG Bewerker', 'pages_edit_switch_to_wysiwyg' => 'Schakel naar de WYSIWYG Bewerker',
'pages_edit_switch_to_new_wysiwyg' => 'Schakel naar de nieuwe WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Schakel naar de nieuwe WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta-testfase)',
'pages_edit_set_changelog' => 'Logboek instellen', 'pages_edit_set_changelog' => 'Logboek instellen',
'pages_edit_enter_changelog_desc' => 'Geef een korte omschrijving van de wijzigingen die je gemaakt hebt', 'pages_edit_enter_changelog_desc' => 'Geef een korte omschrijving van de wijzigingen die je gemaakt hebt',
'pages_edit_enter_changelog' => 'Voeg toe aan logboek', 'pages_edit_enter_changelog' => 'Voeg toe aan logboek',
@@ -392,11 +392,11 @@ return [
'comment' => 'Reactie', 'comment' => 'Reactie',
'comments' => 'Reacties', 'comments' => 'Reacties',
'comment_add' => 'Reactie toevoegen', 'comment_add' => 'Reactie toevoegen',
'comment_none' => 'No comments to display', 'comment_none' => 'Geen opmerkingen om weer te geven',
'comment_placeholder' => 'Laat hier een reactie achter', 'comment_placeholder' => 'Laat hier een reactie achter',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count Reactie Thread|:count Reactie Threads',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count Gearchiveerd',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Gearchiveerde Threads',
'comment_save' => 'Sla reactie op', 'comment_save' => 'Sla reactie op',
'comment_new' => 'Nieuwe reactie', 'comment_new' => 'Nieuwe reactie',
'comment_created' => 'reactie gegeven :createDiff', 'comment_created' => 'reactie gegeven :createDiff',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Reactie verwijderd', 'comment_deleted_success' => 'Reactie verwijderd',
'comment_created_success' => 'Reactie toegevoegd', 'comment_created_success' => 'Reactie toegevoegd',
'comment_updated_success' => 'Reactie bijgewerkt', 'comment_updated_success' => 'Reactie bijgewerkt',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Opmerking gearchiveerd',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Opmerking teruggehaald',
'comment_view' => 'View comment', 'comment_view' => 'Opmerking weergeven',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Ga naar thread',
'comment_delete_confirm' => 'Weet je zeker dat je deze reactie wilt verwijderen?', 'comment_delete_confirm' => 'Weet je zeker dat je deze reactie wilt verwijderen?',
'comment_in_reply_to' => 'Als antwoord op :commentId', 'comment_in_reply_to' => 'Als antwoord op :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Verwijzing',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Verouderd)',
'comment_editor_explain' => 'Hier zijn de opmerkingen die zijn achtergelaten op deze pagina. Opmerkingen kunnen worden toegevoegd en beheerd wanneer u de opgeslagen pagina bekijkt.', 'comment_editor_explain' => 'Hier zijn de opmerkingen die zijn achtergelaten op deze pagina. Opmerkingen kunnen worden toegevoegd en beheerd wanneer u de opgeslagen pagina bekijkt.',
// Revision // Revision

View File

@@ -35,8 +35,8 @@ return [
'create' => 'Criar', 'create' => 'Criar',
'update' => 'Atualizar', 'update' => 'Atualizar',
'edit' => 'Editar', 'edit' => 'Editar',
'archive' => 'Archive', 'archive' => 'Arquivar',
'unarchive' => 'Un-Archive', 'unarchive' => 'Desarquivar',
'sort' => 'Ordenar', 'sort' => 'Ordenar',
'move' => 'Mover', 'move' => 'Mover',
'copy' => 'Copiar', 'copy' => 'Copiar',

View File

@@ -248,7 +248,7 @@ return [
'pages_edit_switch_to_markdown_stable' => '(Conteúdo Estável)', 'pages_edit_switch_to_markdown_stable' => '(Conteúdo Estável)',
'pages_edit_switch_to_wysiwyg' => 'Alternar para o Editor WYSIWYG', 'pages_edit_switch_to_wysiwyg' => 'Alternar para o Editor WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Mudar para o novo WYSIWYG', 'pages_edit_switch_to_new_wysiwyg' => 'Mudar para o novo WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_switch_to_new_wysiwyg_desc' => '(Em teste beta)',
'pages_edit_set_changelog' => 'Relatar Alterações', 'pages_edit_set_changelog' => 'Relatar Alterações',
'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por você', 'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por você',
'pages_edit_enter_changelog' => 'Insira Alterações', 'pages_edit_enter_changelog' => 'Insira Alterações',
@@ -392,11 +392,11 @@ return [
'comment' => 'Comentário', 'comment' => 'Comentário',
'comments' => 'Comentários', 'comments' => 'Comentários',
'comment_add' => 'Adicionar Comentário', 'comment_add' => 'Adicionar Comentário',
'comment_none' => 'No comments to display', 'comment_none' => 'Nenhum comentário para exibir',
'comment_placeholder' => 'Digite seus comentários aqui', 'comment_placeholder' => 'Digite seus comentários aqui',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads', 'comment_thread_count' => ':count Tópico de Comentário|:count Tópicos de Comentários',
'comment_archived_count' => ':count Archived', 'comment_archived_count' => ':count Arquivado',
'comment_archived_threads' => 'Archived Threads', 'comment_archived_threads' => 'Tópicos Arquivados',
'comment_save' => 'Salvar comentário', 'comment_save' => 'Salvar comentário',
'comment_new' => 'Novo Comentário', 'comment_new' => 'Novo Comentário',
'comment_created' => 'comentado :createDiff', 'comment_created' => 'comentado :createDiff',
@@ -405,14 +405,14 @@ return [
'comment_deleted_success' => 'Comentário removido', 'comment_deleted_success' => 'Comentário removido',
'comment_created_success' => 'Comentário adicionado', 'comment_created_success' => 'Comentário adicionado',
'comment_updated_success' => 'Comentário editado', 'comment_updated_success' => 'Comentário editado',
'comment_archive_success' => 'Comment archived', 'comment_archive_success' => 'Comentário arquivado',
'comment_unarchive_success' => 'Comment un-archived', 'comment_unarchive_success' => 'Comentário desarquivado',
'comment_view' => 'View comment', 'comment_view' => 'Ver comentário',
'comment_jump_to_thread' => 'Jump to thread', 'comment_jump_to_thread' => 'Ir para o tópico',
'comment_delete_confirm' => 'Você tem certeza de que deseja excluir este comentário?', 'comment_delete_confirm' => 'Você tem certeza de que deseja excluir este comentário?',
'comment_in_reply_to' => 'Em resposta à :commentId', 'comment_in_reply_to' => 'Em resposta à :commentId',
'comment_reference' => 'Reference', 'comment_reference' => 'Referência',
'comment_reference_outdated' => '(Outdated)', 'comment_reference_outdated' => '(Desatualizado)',
'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e gerenciados ao visualizar a página salva.', 'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e gerenciados ao visualizar a página salva.',
// Revision // Revision

View File

@@ -47,7 +47,7 @@ return [
'bookshelf_update' => '更新書棧', 'bookshelf_update' => '更新書棧',
'bookshelf_update_notification' => '書棧已更新', 'bookshelf_update_notification' => '書棧已更新',
'bookshelf_delete' => '刪除書棧', 'bookshelf_delete' => '刪除書棧',
'bookshelf_delete_notification' => '書已刪除', 'bookshelf_delete_notification' => '書已刪除',
// Revisions // Revisions
'revision_restore' => '還原的版本', 'revision_restore' => '還原的版本',

View File

@@ -67,7 +67,7 @@ return [
// Entities // Entities
'entity_not_found' => '找不到實體', 'entity_not_found' => '找不到實體',
'bookshelf_not_found' => '未找到書', 'bookshelf_not_found' => '未找到書',
'book_not_found' => '找不到書本', 'book_not_found' => '找不到書本',
'page_not_found' => '找不到頁面', 'page_not_found' => '找不到頁面',
'chapter_not_found' => '找不到章節', 'chapter_not_found' => '找不到章節',

View File

@@ -40,7 +40,7 @@ export class PageComment extends Component {
this.commentId = this.$opts.commentId; this.commentId = this.$opts.commentId;
this.commentLocalId = this.$opts.commentLocalId; this.commentLocalId = this.$opts.commentLocalId;
this.deletedText = this.$opts.deletedText; this.deletedText = this.$opts.deletedText;
this.deletedText = this.$opts.deletedText; this.updatedText = this.$opts.updatedText;
this.archiveText = this.$opts.archiveText; this.archiveText = this.$opts.archiveText;
// Editor reference and text options // Editor reference and text options

View File

@@ -84,7 +84,7 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
// @ts-ignore // @ts-ignore
window.debugEditorState = () => { window.debugEditorState = () => {
console.log(editor.getEditorState().toJSON()); return editor.getEditorState().toJSON();
}; };
registerCommonNodeMutationListeners(context); registerCommonNodeMutationListeners(context);

View File

@@ -355,6 +355,7 @@ function onSelectionChange(
lastNode instanceof ParagraphNode && lastNode instanceof ParagraphNode &&
lastNode.getChildrenSize() === 0 lastNode.getChildrenSize() === 0
) { ) {
selection.format = lastNode.getTextFormat();
selection.style = lastNode.getTextStyle(); selection.style = lastNode.getTextStyle();
} else { } else {
selection.format = 0; selection.format = 0;

View File

@@ -1069,6 +1069,7 @@ describe('LexicalEditor tests', () => {
__prev: null, __prev: null,
__size: 1, __size: 1,
__style: '', __style: '',
__textFormat: 0,
__textStyle: '', __textStyle: '',
__type: 'paragraph', __type: 'paragraph',
}); });
@@ -1149,6 +1150,7 @@ describe('LexicalEditor tests', () => {
__prev: null, __prev: null,
__size: 1, __size: 1,
__style: '', __style: '',
__textFormat: 0,
__textStyle: '', __textStyle: '',
__type: 'paragraph', __type: 'paragraph',
}); });

View File

@@ -76,6 +76,7 @@ describe('LexicalEditorState tests', () => {
__prev: null, __prev: null,
__size: 1, __size: 1,
__style: '', __style: '',
__textFormat: 0,
__textStyle: '', __textStyle: '',
__type: 'paragraph', __type: 'paragraph',
}); });
@@ -111,7 +112,7 @@ describe('LexicalEditorState tests', () => {
}); });
expect(JSON.stringify(editor.getEditorState().toJSON())).toEqual( expect(JSON.stringify(editor.getEditorState().toJSON())).toEqual(
`{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Hello world","type":"text","version":1}],"direction":null,"type":"paragraph","version":1,"id":"","alignment":"","inset":0,"textStyle":""}],"direction":null,"type":"root","version":1}}`, `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Hello world","type":"text","version":1}],"direction":null,"type":"paragraph","version":1,"id":"","alignment":"","inset":0,"textFormat":0,"textStyle":""}],"direction":null,"type":"root","version":1}}`,
); );
}); });

File diff suppressed because one or more lines are too long

View File

@@ -38,6 +38,7 @@ import {DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
import {EditorUiContext} from "../../../../ui/framework/core"; import {EditorUiContext} from "../../../../ui/framework/core";
import {EditorUIManager} from "../../../../ui/framework/manager"; import {EditorUIManager} from "../../../../ui/framework/manager";
import {ImageNode} from "@lexical/rich-text/LexicalImageNode"; import {ImageNode} from "@lexical/rich-text/LexicalImageNode";
import {MediaNode} from "@lexical/rich-text/LexicalMediaNode";
type TestEnv = { type TestEnv = {
readonly container: HTMLDivElement; readonly container: HTMLDivElement;
@@ -487,6 +488,7 @@ export function createTestContext(): EditorUiContext {
theme: {}, theme: {},
nodes: [ nodes: [
ImageNode, ImageNode,
MediaNode,
] ]
}); });

View File

@@ -19,7 +19,7 @@ import type {
LexicalNode, LexicalNode,
NodeKey, NodeKey,
} from '../LexicalNode'; } from '../LexicalNode';
import type {RangeSelection} from 'lexical'; import {RangeSelection, TEXT_TYPE_TO_FORMAT, TextFormatType} from 'lexical';
import { import {
$applyNodeReplacement, $applyNodeReplacement,
@@ -36,6 +36,7 @@ import {CommonBlockNode, copyCommonBlockProperties, SerializedCommonBlockNode} f
export type SerializedParagraphNode = Spread< export type SerializedParagraphNode = Spread<
{ {
textFormat: number;
textStyle: string; textStyle: string;
}, },
SerializedCommonBlockNode SerializedCommonBlockNode
@@ -45,10 +46,12 @@ export type SerializedParagraphNode = Spread<
export class ParagraphNode extends CommonBlockNode { export class ParagraphNode extends CommonBlockNode {
['constructor']!: KlassConstructor<typeof ParagraphNode>; ['constructor']!: KlassConstructor<typeof ParagraphNode>;
/** @internal */ /** @internal */
__textFormat: number;
__textStyle: string; __textStyle: string;
constructor(key?: NodeKey) { constructor(key?: NodeKey) {
super(key); super(key);
this.__textFormat = 0;
this.__textStyle = ''; this.__textStyle = '';
} }
@@ -56,6 +59,22 @@ export class ParagraphNode extends CommonBlockNode {
return 'paragraph'; return 'paragraph';
} }
getTextFormat(): number {
const self = this.getLatest();
return self.__textFormat;
}
setTextFormat(type: number): this {
const self = this.getWritable();
self.__textFormat = type;
return self;
}
hasTextFormat(type: TextFormatType): boolean {
const formatFlag = TEXT_TYPE_TO_FORMAT[type];
return (this.getTextFormat() & formatFlag) !== 0;
}
getTextStyle(): string { getTextStyle(): string {
const self = this.getLatest(); const self = this.getLatest();
return self.__textStyle; return self.__textStyle;
@@ -73,6 +92,7 @@ export class ParagraphNode extends CommonBlockNode {
afterCloneFrom(prevNode: this) { afterCloneFrom(prevNode: this) {
super.afterCloneFrom(prevNode); super.afterCloneFrom(prevNode);
this.__textFormat = prevNode.__textFormat;
this.__textStyle = prevNode.__textStyle; this.__textStyle = prevNode.__textStyle;
copyCommonBlockProperties(prevNode, this); copyCommonBlockProperties(prevNode, this);
} }
@@ -125,12 +145,14 @@ export class ParagraphNode extends CommonBlockNode {
static importJSON(serializedNode: SerializedParagraphNode): ParagraphNode { static importJSON(serializedNode: SerializedParagraphNode): ParagraphNode {
const node = $createParagraphNode(); const node = $createParagraphNode();
deserializeCommonBlockNode(serializedNode, node); deserializeCommonBlockNode(serializedNode, node);
node.setTextFormat(serializedNode.textFormat);
return node; return node;
} }
exportJSON(): SerializedParagraphNode { exportJSON(): SerializedParagraphNode {
return { return {
...super.exportJSON(), ...super.exportJSON(),
textFormat: this.getTextFormat(),
textStyle: this.getTextStyle(), textStyle: this.getTextStyle(),
type: 'paragraph', type: 'paragraph',
version: 1, version: 1,
@@ -144,6 +166,7 @@ export class ParagraphNode extends CommonBlockNode {
restoreSelection: boolean, restoreSelection: boolean,
): ParagraphNode { ): ParagraphNode {
const newElement = $createParagraphNode(); const newElement = $createParagraphNode();
newElement.setTextFormat(rangeSelection.format);
newElement.setTextStyle(rangeSelection.style); newElement.setTextStyle(rangeSelection.style);
const direction = this.getDirection(); const direction = this.getDirection();
newElement.setDirection(direction); newElement.setDirection(direction);

View File

@@ -620,6 +620,7 @@ export class TextNode extends LexicalNode {
// HTML content and not have the ability to use CSS classes. // HTML content and not have the ability to use CSS classes.
exportDOM(editor: LexicalEditor): DOMExportOutput { exportDOM(editor: LexicalEditor): DOMExportOutput {
let {element} = super.exportDOM(editor); let {element} = super.exportDOM(editor);
const originalElementName = (element?.nodeName || '').toLowerCase()
invariant( invariant(
element !== null && isHTMLElement(element), element !== null && isHTMLElement(element),
'Expected TextNode createDOM to always return a HTMLElement', 'Expected TextNode createDOM to always return a HTMLElement',
@@ -649,8 +650,8 @@ export class TextNode extends LexicalNode {
// This is the only way to properly add support for most clients, // This is the only way to properly add support for most clients,
// even if it's semantically incorrect to have to resort to using // even if it's semantically incorrect to have to resort to using
// <b>, <u>, <s>, <i> elements. // <b>, <u>, <s>, <i> elements.
if (this.hasFormat('bold')) { if (this.hasFormat('bold') && originalElementName !== 'strong') {
element = wrapElementWith(element, 'b'); element = wrapElementWith(element, 'strong');
} }
if (this.hasFormat('italic')) { if (this.hasFormat('italic')) {
element = wrapElementWith(element, 'em'); element = wrapElementWith(element, 'em');

View File

@@ -53,6 +53,7 @@ describe('LexicalParagraphNode tests', () => {
direction: null, direction: null,
id: '', id: '',
inset: 0, inset: 0,
textFormat: 0,
textStyle: '', textStyle: '',
type: 'paragraph', type: 'paragraph',
version: 1, version: 1,

View File

@@ -839,7 +839,7 @@ describe('LexicalTextNode tests', () => {
paragraph.append(textNode); paragraph.append(textNode);
const html = $generateHtmlFromNodes($getEditor(), null); const html = $generateHtmlFromNodes($getEditor(), null);
expect(html).toBe('<p><u><em><b><code spellcheck="false"><strong>hello</strong></code></b></em></u></p>'); expect(html).toBe('<p><u><em><strong><code spellcheck="false"><strong>hello</strong></code></strong></em></u></p>');
}); });
}); });

View File

@@ -8,13 +8,12 @@ import {
} from 'lexical'; } from 'lexical';
import type {EditorConfig} from "lexical/LexicalEditor"; import type {EditorConfig} from "lexical/LexicalEditor";
import {el, setOrRemoveAttribute, sizeToPixels} from "../../utils/dom"; import {el, setOrRemoveAttribute, sizeToPixels, styleMapToStyleString, styleStringToStyleMap} from "../../utils/dom";
import { import {
CommonBlockAlignment, deserializeCommonBlockNode, CommonBlockAlignment, deserializeCommonBlockNode,
setCommonBlockPropsFromElement, setCommonBlockPropsFromElement,
updateElementWithCommonBlockProps updateElementWithCommonBlockProps
} from "lexical/nodes/common"; } from "lexical/nodes/common";
import {$selectSingleNode} from "../../utils/selection";
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode"; import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio'; export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
@@ -46,6 +45,19 @@ function filterAttributes(attributes: Record<string, string>): Record<string, st
return filtered; return filtered;
} }
function removeStyleFromAttributes(attributes: Record<string, string>, styleName: string): Record<string, string> {
const attrCopy = Object.assign({}, attributes);
if (!attributes.style) {
return attrCopy;
}
const map = styleStringToStyleMap(attributes.style);
map.delete(styleName);
attrCopy.style = styleMapToStyleString(map);
return attrCopy;
}
function domElementToNode(tag: MediaNodeTag, element: HTMLElement): MediaNode { function domElementToNode(tag: MediaNodeTag, element: HTMLElement): MediaNode {
const node = $createMediaNode(tag); const node = $createMediaNode(tag);
@@ -118,7 +130,7 @@ export class MediaNode extends ElementNode {
getAttributes(): Record<string, string> { getAttributes(): Record<string, string> {
const self = this.getLatest(); const self = this.getLatest();
return self.__attributes; return Object.assign({}, self.__attributes);
} }
setSources(sources: MediaNodeSource[]) { setSources(sources: MediaNodeSource[]) {
@@ -128,25 +140,37 @@ export class MediaNode extends ElementNode {
getSources(): MediaNodeSource[] { getSources(): MediaNodeSource[] {
const self = this.getLatest(); const self = this.getLatest();
return self.__sources; return self.__sources.map(s => Object.assign({}, s))
} }
setSrc(src: string): void { setSrc(src: string): void {
const attrs = Object.assign({}, this.getAttributes()); const attrs = this.getAttributes();
const sources = this.getSources();
if (this.__tag ==='object') { if (this.__tag ==='object') {
attrs.data = src; attrs.data = src;
} if (this.__tag === 'video' && sources.length > 0) {
sources[0].src = src;
delete attrs.src;
if (sources.length > 1) {
sources.splice(1, sources.length - 1);
}
this.setSources(sources);
} else { } else {
attrs.src = src; attrs.src = src;
} }
this.setAttributes(attrs); this.setAttributes(attrs);
} }
setWidthAndHeight(width: string, height: string): void { setWidthAndHeight(width: string, height: string): void {
const attrs = Object.assign( let attrs: Record<string, string> = Object.assign(
{},
this.getAttributes(), this.getAttributes(),
{width, height}, {width, height},
); );
attrs = removeStyleFromAttributes(attrs, 'width');
attrs = removeStyleFromAttributes(attrs, 'height');
this.setAttributes(attrs); this.setAttributes(attrs);
} }
@@ -185,8 +209,8 @@ export class MediaNode extends ElementNode {
return; return;
} }
const attrs = Object.assign({}, this.getAttributes(), {height}); const attrs = Object.assign(this.getAttributes(), {height});
this.setAttributes(attrs); this.setAttributes(removeStyleFromAttributes(attrs, 'height'));
} }
getHeight(): number { getHeight(): number {
@@ -195,8 +219,9 @@ export class MediaNode extends ElementNode {
} }
setWidth(width: number): void { setWidth(width: number): void {
const attrs = Object.assign({}, this.getAttributes(), {width}); const existingAttrs = this.getAttributes();
this.setAttributes(attrs); const attrs: Record<string, string> = Object.assign(existingAttrs, {width});
this.setAttributes(removeStyleFromAttributes(attrs, 'width'));
} }
getWidth(): number { getWidth(): number {
@@ -222,15 +247,9 @@ export class MediaNode extends ElementNode {
createDOM(_config: EditorConfig, _editor: LexicalEditor) { createDOM(_config: EditorConfig, _editor: LexicalEditor) {
const media = this.createInnerDOM(); const media = this.createInnerDOM();
const wrap = el('span', { return el('span', {
class: media.className + ' editor-media-wrap', class: media.className + ' editor-media-wrap',
}, [media]); }, [media]);
wrap.addEventListener('click', e => {
_editor.update(() => $selectSingleNode(this));
});
return wrap;
} }
updateDOM(prevNode: MediaNode, dom: HTMLElement): boolean { updateDOM(prevNode: MediaNode, dom: HTMLElement): boolean {

View File

@@ -0,0 +1,46 @@
import {createTestContext} from "lexical/__tests__/utils";
import {$createMediaNode} from "@lexical/rich-text/LexicalMediaNode";
describe('LexicalMediaNode', () => {
test('setWidth/setHeight/setWidthAndHeight functions remove relevant styles', () => {
const {editor} = createTestContext();
editor.updateAndCommit(() => {
const mediaMode = $createMediaNode('video');
const defaultStyles = {style: 'width:20px;height:40px;color:red'};
mediaMode.setAttributes(defaultStyles);
mediaMode.setWidth(60);
expect(mediaMode.getWidth()).toBe(60);
expect(mediaMode.getAttributes().style).toBe('height:40px;color:red');
mediaMode.setAttributes(defaultStyles);
mediaMode.setHeight(77);
expect(mediaMode.getHeight()).toBe(77);
expect(mediaMode.getAttributes().style).toBe('width:20px;color:red');
mediaMode.setAttributes(defaultStyles);
mediaMode.setWidthAndHeight('6', '7');
expect(mediaMode.getWidth()).toBe(6);
expect(mediaMode.getHeight()).toBe(7);
expect(mediaMode.getAttributes().style).toBe('color:red');
});
});
test('setSrc on video uses sources if existing', () => {
const {editor} = createTestContext();
editor.updateAndCommit(() => {
const mediaMode = $createMediaNode('video');
mediaMode.setAttributes({src: 'z'});
mediaMode.setSources([{src: 'a', type: 'video'}, {src: 'b', type: 'video'}]);
mediaMode.setSrc('c');
expect(mediaMode.getAttributes().src).toBeUndefined();
expect(mediaMode.getSources()).toHaveLength(1);
expect(mediaMode.getSources()[0].src).toBe('c');
});
});
});

View File

@@ -123,6 +123,7 @@ describe('table selection', () => {
__prev: null, __prev: null,
__size: 1, __size: 1,
__style: '', __style: '',
__textFormat: 0,
__textStyle: '', __textStyle: '',
__type: 'paragraph', __type: 'paragraph',
}); });

View File

@@ -92,6 +92,7 @@ export const image: EditorButtonDefinition = {
context.editor.update(() => { context.editor.update(() => {
const link = $createLinkedImageNodeFromImageData(image); const link = $createLinkedImageNodeFromImageData(image);
$insertNodes([link]); $insertNodes([link]);
link.select();
}); });
}) })
}); });

View File

@@ -192,11 +192,17 @@ export function $showMediaForm(media: MediaNode|null, context: EditorUiContext):
let formDefaults = {}; let formDefaults = {};
if (media) { if (media) {
const nodeAttrs = media.getAttributes(); const nodeAttrs = media.getAttributes();
const nodeDOM = media.exportDOM(context.editor).element;
const nodeHtml = (nodeDOM instanceof HTMLElement) ? nodeDOM.outerHTML : '';
formDefaults = { formDefaults = {
src: nodeAttrs.src || nodeAttrs.data || '', src: nodeAttrs.src || nodeAttrs.data || media.getSources()[0]?.src || '',
width: nodeAttrs.width, width: nodeAttrs.width,
height: nodeAttrs.height, height: nodeAttrs.height,
embed: '', embed: nodeHtml,
// This is used so we can check for edits against the embed field on submit
embed_check: nodeHtml,
} }
} }
@@ -214,7 +220,8 @@ export const media: EditorFormDefinition = {
})); }));
const embedCode = (formData.get('embed') || '').toString().trim(); const embedCode = (formData.get('embed') || '').toString().trim();
if (embedCode) { const embedCheck = (formData.get('embed_check') || '').toString().trim();
if (embedCode && embedCode !== embedCheck) {
context.editor.update(() => { context.editor.update(() => {
const node = $createMediaNodeFromHtml(embedCode); const node = $createMediaNodeFromHtml(embedCode);
if (selectedNode && node) { if (selectedNode && node) {
@@ -236,6 +243,7 @@ export const media: EditorFormDefinition = {
if (selectedNode) { if (selectedNode) {
selectedNode.setSrc(src); selectedNode.setSrc(src);
selectedNode.setWidthAndHeight(width, height); selectedNode.setWidthAndHeight(width, height);
context.manager.triggerFutureStateRefresh();
return; return;
} }
@@ -281,6 +289,11 @@ export const media: EditorFormDefinition = {
name: 'embed', name: 'embed',
type: 'textarea', type: 'textarea',
}, },
{
label: '',
name: 'embed_check',
type: 'hidden',
},
], ],
} }
]) ])

View File

@@ -224,6 +224,10 @@ export function getImageToolbarContent(): EditorUiElement[] {
return [new EditorButton(image)]; return [new EditorButton(image)];
} }
export function getMediaToolbarContent(): EditorUiElement[] {
return [new EditorButton(media)];
}
export function getLinkToolbarContent(): EditorUiElement[] { export function getLinkToolbarContent(): EditorUiElement[] {
return [ return [
new EditorButton(link), new EditorButton(link),

View File

@@ -11,7 +11,7 @@ import {el} from "../../utils/dom";
export interface EditorFormFieldDefinition { export interface EditorFormFieldDefinition {
label: string; label: string;
name: string; name: string;
type: 'text' | 'select' | 'textarea' | 'checkbox'; type: 'text' | 'select' | 'textarea' | 'checkbox' | 'hidden';
} }
export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition { export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition {
@@ -67,6 +67,9 @@ export class EditorFormField extends EditorUiElement {
input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'}); input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'});
} else if (this.definition.type === 'checkbox') { } else if (this.definition.type === 'checkbox') {
input = el('input', {id, name: this.definition.name, type: 'checkbox', class: 'editor-form-field-input-checkbox', value: 'true'}); input = el('input', {id, name: this.definition.name, type: 'checkbox', class: 'editor-form-field-input-checkbox', value: 'true'});
} else if (this.definition.type === 'hidden') {
input = el('input', {id, name: this.definition.name, type: 'hidden'});
return el('div', {hidden: 'true'}, [input]);
} else { } else {
input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'}); input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'});
} }

View File

@@ -12,17 +12,21 @@ function isNodeWithSize(node: LexicalNode): node is NodeHasSize&LexicalNode {
class NodeResizer { class NodeResizer {
protected context: EditorUiContext; protected context: EditorUiContext;
protected dom: HTMLElement|null = null; protected resizerDOM: HTMLElement|null = null;
protected targetNode: LexicalNode|null = null;
protected scrollContainer: HTMLElement; protected scrollContainer: HTMLElement;
protected mouseTracker: MouseDragTracker|null = null; protected mouseTracker: MouseDragTracker|null = null;
protected activeSelection: string = ''; protected activeSelection: string = '';
protected loadAbortController = new AbortController();
constructor(context: EditorUiContext) { constructor(context: EditorUiContext) {
this.context = context; this.context = context;
this.scrollContainer = context.scrollDOM; this.scrollContainer = context.scrollDOM;
this.onSelectionChange = this.onSelectionChange.bind(this); this.onSelectionChange = this.onSelectionChange.bind(this);
this.onTargetDOMLoad = this.onTargetDOMLoad.bind(this);
context.manager.onSelectionChange(this.onSelectionChange); context.manager.onSelectionChange(this.onSelectionChange);
} }
@@ -34,12 +38,7 @@ class NodeResizer {
if (nodes.length === 1 && isNodeWithSize(nodes[0])) { if (nodes.length === 1 && isNodeWithSize(nodes[0])) {
const node = nodes[0]; const node = nodes[0];
const nodeKey = node.getKey(); let nodeDOM = this.getTargetDOM(node)
let nodeDOM = this.context.editor.getElementByKey(nodeKey);
if (nodeDOM && nodeDOM.nodeName === 'SPAN') {
nodeDOM = nodeDOM.firstElementChild as HTMLElement;
}
if (nodeDOM) { if (nodeDOM) {
this.showForNode(node, nodeDOM); this.showForNode(node, nodeDOM);
@@ -47,56 +46,81 @@ class NodeResizer {
} }
} }
protected getTargetDOM(targetNode: LexicalNode|null): HTMLElement|null {
if (targetNode == null) {
return null;
}
let nodeDOM = this.context.editor.getElementByKey(targetNode.__key)
if (nodeDOM && nodeDOM.nodeName === 'SPAN') {
nodeDOM = nodeDOM.firstElementChild as HTMLElement;
}
return nodeDOM;
}
protected onTargetDOMLoad(): void {
this.updateResizerPosition();
}
teardown() { teardown() {
this.context.manager.offSelectionChange(this.onSelectionChange); this.context.manager.offSelectionChange(this.onSelectionChange);
this.hide(); this.hide();
} }
protected showForNode(node: NodeHasSize&LexicalNode, dom: HTMLElement) { protected showForNode(node: NodeHasSize&LexicalNode, targetDOM: HTMLElement) {
this.dom = this.buildDOM(); this.resizerDOM = this.buildDOM();
this.targetNode = node;
let ghost = el('span', {class: 'editor-node-resizer-ghost'}); let ghost = el('span', {class: 'editor-node-resizer-ghost'});
if ($isImageNode(node)) { if ($isImageNode(node)) {
ghost = el('img', {src: dom.getAttribute('src'), class: 'editor-node-resizer-ghost'}); ghost = el('img', {src: targetDOM.getAttribute('src'), class: 'editor-node-resizer-ghost'});
} }
this.dom.append(ghost); this.resizerDOM.append(ghost);
this.context.scrollDOM.append(this.dom); this.context.scrollDOM.append(this.resizerDOM);
this.updateDOMPosition(dom); this.updateResizerPosition();
this.mouseTracker = this.setupTracker(this.dom, node, dom); this.mouseTracker = this.setupTracker(this.resizerDOM, node, targetDOM);
this.activeSelection = node.getKey(); this.activeSelection = node.getKey();
if (targetDOM.matches('img, embed, iframe, object')) {
this.loadAbortController = new AbortController();
targetDOM.addEventListener('load', this.onTargetDOMLoad, { signal: this.loadAbortController.signal });
}
} }
protected updateDOMPosition(nodeDOM: HTMLElement) { protected updateResizerPosition() {
if (!this.dom) { const targetDOM = this.getTargetDOM(this.targetNode);
if (!this.resizerDOM || !targetDOM) {
return; return;
} }
const scrollAreaRect = this.scrollContainer.getBoundingClientRect(); const scrollAreaRect = this.scrollContainer.getBoundingClientRect();
const nodeRect = nodeDOM.getBoundingClientRect(); const nodeRect = targetDOM.getBoundingClientRect();
const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop); const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop);
const left = nodeRect.left - scrollAreaRect.left; const left = nodeRect.left - scrollAreaRect.left;
this.dom.style.top = `${top}px`; this.resizerDOM.style.top = `${top}px`;
this.dom.style.left = `${left}px`; this.resizerDOM.style.left = `${left}px`;
this.dom.style.width = nodeRect.width + 'px'; this.resizerDOM.style.width = nodeRect.width + 'px';
this.dom.style.height = nodeRect.height + 'px'; this.resizerDOM.style.height = nodeRect.height + 'px';
} }
protected updateDOMSize(width: number, height: number): void { protected updateDOMSize(width: number, height: number): void {
if (!this.dom) { if (!this.resizerDOM) {
return; return;
} }
this.dom.style.width = width + 'px'; this.resizerDOM.style.width = width + 'px';
this.dom.style.height = height + 'px'; this.resizerDOM.style.height = height + 'px';
} }
protected hide() { protected hide() {
this.mouseTracker?.teardown(); this.mouseTracker?.teardown();
this.dom?.remove(); this.resizerDOM?.remove();
this.targetNode = null;
this.activeSelection = ''; this.activeSelection = '';
this.loadAbortController.abort();
} }
protected buildDOM() { protected buildDOM() {
@@ -110,7 +134,7 @@ class NodeResizer {
}, handleElems); }, handleElems);
} }
setupTracker(container: HTMLElement, node: NodeHasSize, nodeDOM: HTMLElement): MouseDragTracker { setupTracker(container: HTMLElement, node: NodeHasSize&LexicalNode, nodeDOM: HTMLElement): MouseDragTracker {
let startingWidth: number = 0; let startingWidth: number = 0;
let startingHeight: number = 0; let startingHeight: number = 0;
let startingRatio: number = 0; let startingRatio: number = 0;
@@ -140,7 +164,7 @@ class NodeResizer {
return new MouseDragTracker(container, '.editor-node-resizer-handle', { return new MouseDragTracker(container, '.editor-node-resizer-handle', {
down(event: MouseEvent, handle: HTMLElement) { down(event: MouseEvent, handle: HTMLElement) {
_this.dom?.classList.add('active'); _this.resizerDOM?.classList.add('active');
_this.context.editor.getEditorState().read(() => { _this.context.editor.getEditorState().read(() => {
const domRect = nodeDOM.getBoundingClientRect(); const domRect = nodeDOM.getBoundingClientRect();
startingWidth = node.getWidth() || domRect.width; startingWidth = node.getWidth() || domRect.width;
@@ -163,12 +187,15 @@ class NodeResizer {
_this.context.editor.update(() => { _this.context.editor.update(() => {
node.setWidth(size.width); node.setWidth(size.width);
node.setHeight(hasHeight ? size.height : 0); node.setHeight(hasHeight ? size.height : 0);
_this.context.manager.triggerLayoutUpdate(); }, {
requestAnimationFrame(() => { onUpdate: () => {
_this.updateDOMPosition(nodeDOM); requestAnimationFrame(() => {
}) _this.context.manager.triggerLayoutUpdate();
_this.updateResizerPosition();
});
}
}); });
_this.dom?.classList.remove('active'); _this.resizerDOM?.classList.remove('active');
} }
}); });
} }

View File

@@ -15,6 +15,7 @@ class TableResizer {
protected targetCell: HTMLElement|null = null; protected targetCell: HTMLElement|null = null;
protected xMarkerAtStart : boolean = false; protected xMarkerAtStart : boolean = false;
protected yMarkerAtStart : boolean = false; protected yMarkerAtStart : boolean = false;
protected activeInTable: boolean = false;
constructor(editor: LexicalEditor, editScrollContainer: HTMLElement) { constructor(editor: LexicalEditor, editScrollContainer: HTMLElement) {
this.editor = editor; this.editor = editor;
@@ -33,9 +34,10 @@ class TableResizer {
} }
protected setupListeners() { protected setupListeners() {
this.onTableMouseOver = this.onTableMouseOver.bind(this);
this.onCellMouseMove = this.onCellMouseMove.bind(this); this.onCellMouseMove = this.onCellMouseMove.bind(this);
this.onScrollOrResize = this.onScrollOrResize.bind(this); this.onScrollOrResize = this.onScrollOrResize.bind(this);
this.editScrollContainer.addEventListener('mousemove', this.onCellMouseMove); this.editScrollContainer.addEventListener('mouseover', this.onTableMouseOver, { passive: true });
window.addEventListener('scroll', this.onScrollOrResize, {capture: true, passive: true}); window.addEventListener('scroll', this.onScrollOrResize, {capture: true, passive: true});
window.addEventListener('resize', this.onScrollOrResize, {passive: true}); window.addEventListener('resize', this.onScrollOrResize, {passive: true});
} }
@@ -44,8 +46,26 @@ class TableResizer {
this.updateCurrentMarkerTargetPosition(); this.updateCurrentMarkerTargetPosition();
} }
protected onTableMouseOver(event: MouseEvent): void {
if (this.dragging) {
return;
}
const table = (event.target as HTMLElement).closest('table') as HTMLElement|null;
if (table && !this.activeInTable) {
this.editScrollContainer.addEventListener('mousemove', this.onCellMouseMove, { passive: true });
this.onCellMouseMove(event);
this.activeInTable = true;
} else if (!table && this.activeInTable) {
this.editScrollContainer.removeEventListener('mousemove', this.onCellMouseMove);
this.hideMarkers();
this.activeInTable = false;
}
}
protected onCellMouseMove(event: MouseEvent) { protected onCellMouseMove(event: MouseEvent) {
const cell = (event.target as HTMLElement).closest('td,th') as HTMLElement; const cell = (event.target as HTMLElement).closest('td,th') as HTMLElement|null;
if (!cell || this.dragging) { if (!cell || this.dragging) {
return; return;
} }
@@ -66,10 +86,16 @@ class TableResizer {
protected updateMarkersTo(cell: HTMLElement, xPos: number, yPos: number) { protected updateMarkersTo(cell: HTMLElement, xPos: number, yPos: number) {
const markers: MarkerDomRecord = this.getMarkers(); const markers: MarkerDomRecord = this.getMarkers();
const table = cell.closest('table') as HTMLElement; const table = cell.closest('table') as HTMLElement;
const caption: HTMLTableCaptionElement|null = table.querySelector('caption');
const tableRect = table.getBoundingClientRect(); const tableRect = table.getBoundingClientRect();
const editBounds = this.editScrollContainer.getBoundingClientRect(); const editBounds = this.editScrollContainer.getBoundingClientRect();
const maxTop = Math.max(tableRect.top, editBounds.top); let tableTop = tableRect.top;
if (caption) {
tableTop = caption.getBoundingClientRect().bottom;
}
const maxTop = Math.max(tableTop, editBounds.top);
const maxBottom = Math.min(tableRect.bottom, editBounds.bottom); const maxBottom = Math.min(tableRect.bottom, editBounds.bottom);
const maxHeight = maxBottom - maxTop; const maxHeight = maxBottom - maxTop;
markers.x.style.left = xPos + 'px'; markers.x.style.left = xPos + 'px';
@@ -85,6 +111,13 @@ class TableResizer {
markers.x.hidden = tableRect.top > editBounds.bottom || tableRect.bottom < editBounds.top; markers.x.hidden = tableRect.top > editBounds.bottom || tableRect.bottom < editBounds.top;
} }
protected hideMarkers(): void {
if (this.markerDom) {
this.markerDom.x.hidden = true;
this.markerDom.y.hidden = true;
}
}
protected updateCurrentMarkerTargetPosition(): void { protected updateCurrentMarkerTargetPosition(): void {
if (!this.targetCell) { if (!this.targetCell) {
return; return;

View File

@@ -34,7 +34,11 @@ export class EditorContextToolbar extends EditorContainerUiElement {
dom.hidden = !showing; dom.hidden = !showing;
if (!showing) { if (!this.target.isConnected) {
// If our target is no longer in the DOM, tell the manager an update is needed.
this.getContext().manager.triggerFutureStateRefresh();
return;
} else if (!showing) {
return; return;
} }

View File

@@ -3,7 +3,7 @@ import {
getCodeToolbarContent, getDetailsToolbarContent, getCodeToolbarContent, getDetailsToolbarContent,
getImageToolbarContent, getImageToolbarContent,
getLinkToolbarContent, getLinkToolbarContent,
getMainEditorFullToolbar, getTableToolbarContent getMainEditorFullToolbar, getMediaToolbarContent, getTableToolbarContent
} from "./defaults/toolbars"; } from "./defaults/toolbars";
import {EditorUIManager} from "./framework/manager"; import {EditorUIManager} from "./framework/manager";
import {EditorUiContext} from "./framework/core"; import {EditorUiContext} from "./framework/core";
@@ -44,6 +44,10 @@ export function buildEditorUI(container: HTMLElement, element: HTMLElement, scro
selector: 'img:not([drawio-diagram] img)', selector: 'img:not([drawio-diagram] img)',
content: getImageToolbarContent(), content: getImageToolbarContent(),
}); });
manager.registerContextToolbar('media', {
selector: '.editor-media-wrap',
content: getMediaToolbarContent(),
});
manager.registerContextToolbar('link', { manager.registerContextToolbar('link', {
selector: 'a', selector: 'a',
content: getLinkToolbarContent(), content: getLinkToolbarContent(),

View File

@@ -52,12 +52,19 @@ export type StyleMap = Map<string, string>;
/** /**
* Creates a map from an element's styles. * Creates a map from an element's styles.
* Uses direct attribute value string handling since attempting to iterate * Uses direct attribute value string handling since attempting to iterate
* over .style will expand out any shorthand properties (like 'padding') making * over .style will expand out any shorthand properties (like 'padding')
* rather than being representative of the actual properties set. * rather than being representative of the actual properties set.
*/ */
export function extractStyleMapFromElement(element: HTMLElement): StyleMap { export function extractStyleMapFromElement(element: HTMLElement): StyleMap {
const map: StyleMap = new Map();
const styleText= element.getAttribute('style') || ''; const styleText= element.getAttribute('style') || '';
return styleStringToStyleMap(styleText);
}
/**
* Convert string-formatted styles into a StyleMap.
*/
export function styleStringToStyleMap(styleText: string): StyleMap {
const map: StyleMap = new Map();
const rules = styleText.split(';'); const rules = styleText.split(';');
for (const rule of rules) { for (const rule of rules) {
@@ -72,6 +79,17 @@ export function extractStyleMapFromElement(element: HTMLElement): StyleMap {
return map; return map;
} }
/**
* Convert a StyleMap into inline string style text.
*/
export function styleMapToStyleString(map: StyleMap): string {
const parts = [];
for (const [style, value] of map.entries()) {
parts.push(`${style}:${value}`);
}
return parts.join(';');
}
export function setOrRemoveAttribute(element: HTMLElement, name: string, value: string|null|undefined) { export function setOrRemoveAttribute(element: HTMLElement, name: string, value: string|null|undefined) {
if (value) { if (value) {
element.setAttribute(name, value); element.setAttribute(name, value);

View File

@@ -3,7 +3,7 @@ import {
$createParagraphNode, $createRangeSelection, $createParagraphNode, $createRangeSelection,
$getRoot, $getRoot,
$getSelection, $isBlockElementNode, $isDecoratorNode, $getSelection, $isBlockElementNode, $isDecoratorNode,
$isElementNode, $isElementNode, $isParagraphNode,
$isTextNode, $isTextNode,
$setSelection, $setSelection,
BaseSelection, DecoratorNode, BaseSelection, DecoratorNode,
@@ -60,12 +60,19 @@ export function $selectionContainsTextFormat(selection: BaseSelection | null, fo
return false; return false;
} }
for (const node of selection.getNodes()) { // Check text nodes
const nodes = selection.getNodes();
for (const node of nodes) {
if ($isTextNode(node) && node.hasFormat(format)) { if ($isTextNode(node) && node.hasFormat(format)) {
return true; return true;
} }
} }
// If we're in an empty paragraph, check the paragraph format
if (nodes.length === 1 && $isParagraphNode(nodes[0]) && nodes[0].hasTextFormat(format)) {
return true;
}
return false; return false;
} }

View File

@@ -454,7 +454,7 @@ body.editor-is-fullscreen {
.editor-media-wrap { .editor-media-wrap {
display: inline-block; display: inline-block;
cursor: not-allowed; cursor: not-allowed;
iframe { iframe, video {
pointer-events: none; pointer-events: none;
} }
&.align-left { &.align-left {

View File

@@ -106,4 +106,14 @@ class SearchIndexingTest extends TestCase
$this->assertNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is not indexed"); $this->assertNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is not indexed");
} }
} }
public function test_non_breaking_spaces_handled_as_spaces()
{
$page = $this->entities->newPage(['html' => '<p>a&nbsp;tigerbadger is a dangerous&nbsp;animal</p>']);
$scoreByTerm = $page->searchTerms()->pluck('score', 'term');
$this->assertNotNull($scoreByTerm->get('tigerbadger'));
$this->assertNotNull($scoreByTerm->get('dangerous'));
$this->assertNotNull($scoreByTerm->get('animal'));
}
} }

View File

@@ -0,0 +1,56 @@
<?php
namespace Tests\Uploads;
use BookStack\Exceptions\DrawioPngReaderException;
use BookStack\Uploads\DrawioPngReader;
use Tests\TestCase;
class DrawioPngReaderTest extends TestCase
{
public function test_exact_drawing()
{
$file = $this->files->testFilePath('test.drawio.png');
$stream = fopen($file, 'r');
$reader = new DrawioPngReader($stream);
$drawing = $reader->extractDrawing();
$this->assertStringStartsWith('<mxfile ', $drawing);
$this->assertStringEndsWith("</mxfile>\n", $drawing);
}
public function test_extract_drawing_with_non_drawing_image_throws_exception()
{
$file = $this->files->testFilePath('test-image.png');
$stream = fopen($file, 'r');
$reader = new DrawioPngReader($stream);
$exception = null;
try {
$drawing = $reader->extractDrawing();
} catch (\Exception $e) {
$exception = $e;
}
$this->assertInstanceOf(DrawioPngReaderException::class, $exception);
$this->assertEquals($exception->getMessage(), 'Unable to find drawing data within PNG file');
}
public function test_extract_drawing_with_non_png_image_throws_exception()
{
$file = $this->files->testFilePath('test-image.jpg');
$stream = fopen($file, 'r');
$reader = new DrawioPngReader($stream);
$exception = null;
try {
$drawing = $reader->extractDrawing();
} catch (\Exception $e) {
$exception = $e;
}
$this->assertInstanceOf(DrawioPngReaderException::class, $exception);
$this->assertEquals($exception->getMessage(), 'File does not appear to be a valid PNG file');
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB