', false);
+ }
+
+ public function test_revision_changes_view_filters_html_content()
{
$this->asEditor();
$page = $this->entities->page();
@@ -237,7 +250,7 @@ class PageRevisionTest extends TestCase
$this->createRevisions($page, 1, ['name' => 'updated page', 'html' => $html]);
$this->createRevisions($page, 1, ['name' => 'updated page', 'html' => $html]);
- $pageRevision = $page->revisions->last();
+ $pageRevision = $page->revisions()->orderBy('id', 'desc')->first();
$revisionView = $this->get("{$page->getUrl()}/revisions/{$pageRevision->id}/changes");
$revisionView->assertStatus(200);
$revisionView->assertSee('expectthisthough');
From 6216c89f82ed66a7b2a4aae1fda67233a554ab20 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Tue, 10 Mar 2026 17:48:12 +0000
Subject: [PATCH 03/85] Packages: Updated PHP package versions
---
composer.lock | 222 +++++++++++++++++++++++++-------------------------
1 file changed, 112 insertions(+), 110 deletions(-)
diff --git a/composer.lock b/composer.lock
index 18d0da621..8ee6233b8 100644
--- a/composer.lock
+++ b/composer.lock
@@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.371.2",
+ "version": "3.372.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "32090a8ac3ec8859cb83bdde800b8f0ecf92d8ec"
+ "reference": "d1885c8c5db03c2a23121e6df58ef5693df41b95"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/32090a8ac3ec8859cb83bdde800b8f0ecf92d8ec",
- "reference": "32090a8ac3ec8859cb83bdde800b8f0ecf92d8ec",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d1885c8c5db03c2a23121e6df58ef5693df41b95",
+ "reference": "d1885c8c5db03c2a23121e6df58ef5693df41b95",
"shasum": ""
},
"require": {
@@ -135,11 +135,11 @@
"authors": [
{
"name": "Amazon Web Services",
- "homepage": "http://aws.amazon.com"
+ "homepage": "https://aws.amazon.com"
}
],
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
- "homepage": "http://aws.amazon.com/sdkforphp",
+ "homepage": "https://aws.amazon.com/sdk-for-php",
"keywords": [
"amazon",
"aws",
@@ -153,9 +153,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.371.2"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.372.2"
},
- "time": "2026-02-26T19:06:10+00:00"
+ "time": "2026-03-09T18:21:50+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -635,16 +635,16 @@
},
{
"name": "dompdf/dompdf",
- "version": "v3.1.4",
+ "version": "v3.1.5",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
- "reference": "db712c90c5b9868df3600e64e68da62e78a34623"
+ "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623",
- "reference": "db712c90c5b9868df3600e64e68da62e78a34623",
+ "url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
+ "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496",
"shasum": ""
},
"require": {
@@ -693,9 +693,9 @@
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
- "source": "https://github.com/dompdf/dompdf/tree/v3.1.4"
+ "source": "https://github.com/dompdf/dompdf/tree/v3.1.5"
},
- "time": "2025-10-29T12:43:30+00:00"
+ "time": "2026-03-03T13:54:37+00:00"
},
{
"name": "dompdf/php-font-lib",
@@ -1387,16 +1387,16 @@
},
{
"name": "guzzlehttp/psr7",
- "version": "2.8.0",
+ "version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "21dc724a0583619cd1652f673303492272778051"
+ "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
- "reference": "21dc724a0583619cd1652f673303492272778051",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884",
+ "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884",
"shasum": ""
},
"require": {
@@ -1412,6 +1412,7 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
+ "jshttp/mime-db": "1.54.0.1",
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"suggest": {
@@ -1483,7 +1484,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.8.0"
+ "source": "https://github.com/guzzle/psr7/tree/2.9.0"
},
"funding": [
{
@@ -1499,7 +1500,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-23T21:21:41+00:00"
+ "time": "2026-03-10T16:41:02+00:00"
},
{
"name": "guzzlehttp/uri-template",
@@ -1800,16 +1801,16 @@
},
{
"name": "laravel/framework",
- "version": "v12.53.0",
+ "version": "v12.54.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "f57f035c0d34503d9ff30be76159bb35a003cd1f"
+ "reference": "e908e117421bcade301b174aba2d3e8cc1e1f213"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/f57f035c0d34503d9ff30be76159bb35a003cd1f",
- "reference": "f57f035c0d34503d9ff30be76159bb35a003cd1f",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/e908e117421bcade301b174aba2d3e8cc1e1f213",
+ "reference": "e908e117421bcade301b174aba2d3e8cc1e1f213",
"shasum": ""
},
"require": {
@@ -1830,7 +1831,7 @@
"guzzlehttp/uri-template": "^1.0",
"laravel/prompts": "^0.3.0",
"laravel/serializable-closure": "^1.3|^2.0",
- "league/commonmark": "^2.7",
+ "league/commonmark": "^2.8.1",
"league/flysystem": "^3.25.1",
"league/flysystem-local": "^3.25.1",
"league/uri": "^7.5.1",
@@ -2018,20 +2019,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-02-24T14:35:15+00:00"
+ "time": "2026-03-10T15:30:40+00:00"
},
{
"name": "laravel/prompts",
- "version": "v0.3.13",
+ "version": "v0.3.14",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
- "reference": "ed8c466571b37e977532fb2fd3c272c784d7050d"
+ "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/prompts/zipball/ed8c466571b37e977532fb2fd3c272c784d7050d",
- "reference": "ed8c466571b37e977532fb2fd3c272c784d7050d",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
+ "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
"shasum": ""
},
"require": {
@@ -2075,9 +2076,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
- "source": "https://github.com/laravel/prompts/tree/v0.3.13"
+ "source": "https://github.com/laravel/prompts/tree/v0.3.14"
},
- "time": "2026-02-06T12:17:10+00:00"
+ "time": "2026-03-01T09:02:38+00:00"
},
{
"name": "laravel/serializable-closure",
@@ -2142,16 +2143,16 @@
},
{
"name": "laravel/socialite",
- "version": "v5.24.3",
+ "version": "v5.25.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/socialite.git",
- "reference": "0feb62267e7b8abc68593ca37639ad302728c129"
+ "reference": "231f572e1a37c9ca1fb8085e9fb8608285beafb3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/socialite/zipball/0feb62267e7b8abc68593ca37639ad302728c129",
- "reference": "0feb62267e7b8abc68593ca37639ad302728c129",
+ "url": "https://api.github.com/repos/laravel/socialite/zipball/231f572e1a37c9ca1fb8085e9fb8608285beafb3",
+ "reference": "231f572e1a37c9ca1fb8085e9fb8608285beafb3",
"shasum": ""
},
"require": {
@@ -2210,7 +2211,7 @@
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
- "time": "2026-02-21T13:32:50+00:00"
+ "time": "2026-02-27T13:56:35+00:00"
},
{
"name": "laravel/tinker",
@@ -2280,16 +2281,16 @@
},
{
"name": "league/commonmark",
- "version": "2.8.0",
+ "version": "2.8.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb"
+ "reference": "84b1ca48347efdbe775426f108622a42735a6579"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb",
- "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579",
+ "reference": "84b1ca48347efdbe775426f108622a42735a6579",
"shasum": ""
},
"require": {
@@ -2314,9 +2315,9 @@
"phpstan/phpstan": "^1.8.2",
"phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
"scrutinizer/ocular": "^1.8.1",
- "symfony/finder": "^5.3 | ^6.0 | ^7.0",
- "symfony/process": "^5.4 | ^6.0 | ^7.0",
- "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0",
"unleashedtech/php-coding-standard": "^3.1.1",
"vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
},
@@ -2383,7 +2384,7 @@
"type": "tidelift"
}
],
- "time": "2025-11-26T21:48:24+00:00"
+ "time": "2026-03-05T21:37:03+00:00"
},
{
"name": "league/config",
@@ -4188,16 +4189,16 @@
},
{
"name": "predis/predis",
- "version": "v3.4.1",
+ "version": "v3.4.2",
"source": {
"type": "git",
"url": "https://github.com/predis/predis.git",
- "reference": "0850f2f36ee179f0ff96c92c750e1366c6cd754c"
+ "reference": "2033429520d8997a7815a2485f56abe6d2d0e075"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/predis/predis/zipball/0850f2f36ee179f0ff96c92c750e1366c6cd754c",
- "reference": "0850f2f36ee179f0ff96c92c750e1366c6cd754c",
+ "url": "https://api.github.com/repos/predis/predis/zipball/2033429520d8997a7815a2485f56abe6d2d0e075",
+ "reference": "2033429520d8997a7815a2485f56abe6d2d0e075",
"shasum": ""
},
"require": {
@@ -4239,7 +4240,7 @@
],
"support": {
"issues": "https://github.com/predis/predis/issues",
- "source": "https://github.com/predis/predis/tree/v3.4.1"
+ "source": "https://github.com/predis/predis/tree/v3.4.2"
},
"funding": [
{
@@ -4247,7 +4248,7 @@
"type": "github"
}
],
- "time": "2026-02-23T19:51:21+00:00"
+ "time": "2026-03-09T20:33:04+00:00"
},
{
"name": "psr/clock",
@@ -4663,16 +4664,16 @@
},
{
"name": "psy/psysh",
- "version": "v0.12.20",
+ "version": "v0.12.21",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
- "reference": "19678eb6b952a03b8a1d96ecee9edba518bb0373"
+ "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bobthecow/psysh/zipball/19678eb6b952a03b8a1d96ecee9edba518bb0373",
- "reference": "19678eb6b952a03b8a1d96ecee9edba518bb0373",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4821fab5b7cd8c49a673a9fd5754dc9162bb9e97",
+ "reference": "4821fab5b7cd8c49a673a9fd5754dc9162bb9e97",
"shasum": ""
},
"require": {
@@ -4736,9 +4737,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
- "source": "https://github.com/bobthecow/psysh/tree/v0.12.20"
+ "source": "https://github.com/bobthecow/psysh/tree/v0.12.21"
},
- "time": "2026-02-11T15:05:28+00:00"
+ "time": "2026-03-06T21:21:28+00:00"
},
{
"name": "ralouphie/getallheaders",
@@ -4982,16 +4983,16 @@
},
{
"name": "sabberworm/php-css-parser",
- "version": "v9.2.0",
+ "version": "v9.3.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
- "reference": "59373045e11ad47b5c18fc615feee0219e42f6d3"
+ "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/59373045e11ad47b5c18fc615feee0219e42f6d3",
- "reference": "59373045e11ad47b5c18fc615feee0219e42f6d3",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
+ "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949",
"shasum": ""
},
"require": {
@@ -5018,7 +5019,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "9.3.x-dev"
+ "dev-main": "9.4.x-dev"
}
},
"autoload": {
@@ -5056,9 +5057,9 @@
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
- "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.2.0"
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0"
},
- "time": "2026-02-21T17:12:03+00:00"
+ "time": "2026-03-03T17:31:43+00:00"
},
{
"name": "socialiteproviders/discord",
@@ -5508,16 +5509,16 @@
},
{
"name": "symfony/console",
- "version": "v7.4.6",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "6d643a93b47398599124022eb24d97c153c12f27"
+ "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/6d643a93b47398599124022eb24d97c153c12f27",
- "reference": "6d643a93b47398599124022eb24d97c153c12f27",
+ "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d",
+ "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d",
"shasum": ""
},
"require": {
@@ -5582,7 +5583,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.4.6"
+ "source": "https://github.com/symfony/console/tree/v7.4.7"
},
"funding": [
{
@@ -5602,7 +5603,7 @@
"type": "tidelift"
}
],
- "time": "2026-02-25T17:02:47+00:00"
+ "time": "2026-03-06T14:06:20+00:00"
},
{
"name": "symfony/css-selector",
@@ -6123,16 +6124,16 @@
},
{
"name": "symfony/http-foundation",
- "version": "v7.4.6",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065"
+ "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fd97d5e926e988a363cef56fbbf88c5c528e9065",
- "reference": "fd97d5e926e988a363cef56fbbf88c5c528e9065",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f94b3e7b7dafd40e666f0c9ff2084133bae41e81",
+ "reference": "f94b3e7b7dafd40e666f0c9ff2084133bae41e81",
"shasum": ""
},
"require": {
@@ -6181,7 +6182,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v7.4.6"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.4.7"
},
"funding": [
{
@@ -6201,20 +6202,20 @@
"type": "tidelift"
}
],
- "time": "2026-02-21T16:25:55+00:00"
+ "time": "2026-03-06T13:15:18+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v7.4.6",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "002ac0cf4cd972a7fd0912dcd513a95e8a81ce83"
+ "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/002ac0cf4cd972a7fd0912dcd513a95e8a81ce83",
- "reference": "002ac0cf4cd972a7fd0912dcd513a95e8a81ce83",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3b3fcf386c809be990c922e10e4c620d6367cab1",
+ "reference": "3b3fcf386c809be990c922e10e4c620d6367cab1",
"shasum": ""
},
"require": {
@@ -6300,7 +6301,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v7.4.6"
+ "source": "https://github.com/symfony/http-kernel/tree/v7.4.7"
},
"funding": [
{
@@ -6320,7 +6321,7 @@
"type": "tidelift"
}
],
- "time": "2026-02-26T08:30:57+00:00"
+ "time": "2026-03-06T16:33:18+00:00"
},
{
"name": "symfony/mailer",
@@ -6408,16 +6409,16 @@
},
{
"name": "symfony/mime",
- "version": "v7.4.6",
+ "version": "v7.4.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "9fc881d95feae4c6c48678cb6372bd8a7ba04f5f"
+ "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/9fc881d95feae4c6c48678cb6372bd8a7ba04f5f",
- "reference": "9fc881d95feae4c6c48678cb6372bd8a7ba04f5f",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/da5ab4fde3f6c88ab06e96185b9922f48b677cd1",
+ "reference": "da5ab4fde3f6c88ab06e96185b9922f48b677cd1",
"shasum": ""
},
"require": {
@@ -6473,7 +6474,7 @@
"mime-type"
],
"support": {
- "source": "https://github.com/symfony/mime/tree/v7.4.6"
+ "source": "https://github.com/symfony/mime/tree/v7.4.7"
},
"funding": [
{
@@ -6493,7 +6494,7 @@
"type": "tidelift"
}
],
- "time": "2026-02-05T15:57:06+00:00"
+ "time": "2026-03-05T15:24:09+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -8721,40 +8722,40 @@
},
{
"name": "larastan/larastan",
- "version": "v3.9.2",
+ "version": "v3.9.3",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
- "reference": "2e9ed291bdc1969e7f270fb33c9cdf3c912daeb2"
+ "reference": "64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/larastan/larastan/zipball/2e9ed291bdc1969e7f270fb33c9cdf3c912daeb2",
- "reference": "2e9ed291bdc1969e7f270fb33c9cdf3c912daeb2",
+ "url": "https://api.github.com/repos/larastan/larastan/zipball/64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65",
+ "reference": "64a52bcc5347c89fdf131cb59f96ebfbc8d1ad65",
"shasum": ""
},
"require": {
"ext-json": "*",
"iamcal/sql-parser": "^0.7.0",
- "illuminate/console": "^11.44.2 || ^12.4.1",
- "illuminate/container": "^11.44.2 || ^12.4.1",
- "illuminate/contracts": "^11.44.2 || ^12.4.1",
- "illuminate/database": "^11.44.2 || ^12.4.1",
- "illuminate/http": "^11.44.2 || ^12.4.1",
- "illuminate/pipeline": "^11.44.2 || ^12.4.1",
- "illuminate/support": "^11.44.2 || ^12.4.1",
+ "illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
+ "illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
"php": "^8.2",
"phpstan/phpstan": "^2.1.32"
},
"require-dev": {
"doctrine/coding-standard": "^13",
- "laravel/framework": "^11.44.2 || ^12.7.2",
+ "laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
"mockery/mockery": "^1.6.12",
"nikic/php-parser": "^5.4",
- "orchestra/canvas": "^v9.2.2 || ^10.0.1",
- "orchestra/testbench-core": "^9.12.0 || ^10.1",
+ "orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
+ "orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
"phpstan/phpstan-deprecation-rules": "^2.0.1",
- "phpunit/phpunit": "^10.5.35 || ^11.5.15"
+ "phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8"
},
"suggest": {
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
@@ -8799,7 +8800,7 @@
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
- "source": "https://github.com/larastan/larastan/tree/v3.9.2"
+ "source": "https://github.com/larastan/larastan/tree/v3.9.3"
},
"funding": [
{
@@ -8807,7 +8808,7 @@
"type": "github"
}
],
- "time": "2026-01-30T15:16:32+00:00"
+ "time": "2026-02-20T12:07:12+00:00"
},
{
"name": "mockery/mockery",
@@ -10743,21 +10744,22 @@
},
{
"name": "ssddanbrown/asserthtml",
- "version": "v3.1.0",
+ "version": "v3.2.0",
"source": {
"type": "git",
"url": "https://codeberg.org/danb/asserthtml",
- "reference": "cf8206171d667d43e1bdde17d67191f30e95c8a0"
+ "reference": "0811b5c8d541f344c193bd7f2c2d79d13d23b141"
},
"dist": {
"type": "zip",
- "url": "https://codeberg.org/api/v1/repos/danb/asserthtml/archive/%prettyVersion%.zip"
+ "url": "https://codeberg.org/api/v1/repos/danb/asserthtml/archive/%prettyVersion%.zip",
+ "reference": "0811b5c8d541f344c193bd7f2c2d79d13d23b141"
},
"require": {
"ext-dom": "*",
"ext-json": "*",
"php": ">=8.1",
- "phpunit/phpunit": "^10.0|^11.0",
+ "phpunit/phpunit": "^10.0|^11.0|^12.0|^13.0",
"symfony/css-selector": "^6.0|^7.0",
"symfony/dom-crawler": "^6.0|^7.0"
},
@@ -10783,7 +10785,7 @@
],
"description": "HTML Content Assertions for PHPUnit",
"homepage": "https://codeberg.org/danb/asserthtml",
- "time": "2025-01-11T13:35:55+00:00"
+ "time": "2026-03-04T14:19:44+00:00"
},
{
"name": "staabm/side-effects-detector",
From 6e7cc169d1527c18556d536159e0e23df85c381e Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Tue, 10 Mar 2026 18:31:51 +0000
Subject: [PATCH 04/85] Preferences: Updated return redirect with better origin
checks
As suggested by Alex Dan in their security report.
---
app/Http/Controller.php | 16 ++++++++++++++--
tests/User/UserPreferencesTest.php | 20 ++++++++++++++++++++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/app/Http/Controller.php b/app/Http/Controller.php
index 5d3be4951..1a0f5932e 100644
--- a/app/Http/Controller.php
+++ b/app/Http/Controller.php
@@ -167,14 +167,26 @@ abstract class Controller extends BaseController
/**
* Redirect to the URL provided in the request as a '_return' parameter.
- * Will check that the parameter leads to a URL under the root path of the system.
+ * Will check that the parameter leads to a URL under the same origin as the application.
*/
protected function redirectToRequest(Request $request): RedirectResponse
{
$basePath = url('/');
$returnUrl = $request->input('_return') ?? $basePath;
- if (!str_starts_with($returnUrl, $basePath)) {
+ // Only allow use of _return on requests where we expect CSRF to be active
+ // to prevent it potentially being used as an open redirect
+ $allowedMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
+ if (!in_array($request->getMethod(), $allowedMethods)) {
+ return redirect($basePath);
+ }
+
+ $intendedUrl = parse_url($returnUrl);
+ $baseUrl = parse_url($basePath);
+ $isSameOrigin = ($intendedUrl['host'] ?? '') === ($baseUrl['host'] ?? '')
+ && ($intendedUrl['scheme'] ?? '') === ($baseUrl['scheme'] ?? '')
+ && ($intendedUrl['port'] ?? 0) === ($baseUrl['port'] ?? 0);
+ if (!$isSameOrigin) {
return redirect($basePath);
}
diff --git a/tests/User/UserPreferencesTest.php b/tests/User/UserPreferencesTest.php
index ff3cb63ca..e893f002d 100644
--- a/tests/User/UserPreferencesTest.php
+++ b/tests/User/UserPreferencesTest.php
@@ -153,6 +153,26 @@ class UserPreferencesTest extends TestCase
->assertElementNotExists('.content-wrap .entity-list-item');
}
+ public function test_redirect_on_preference_change_checks_host()
+ {
+ $expectedByRedirect = [
+ 'http://localhost/beans' => 'http://localhost/beans',
+ 'https://localhost/beans' => 'http://localhost',
+ 'http://localhost:9090/beans' => 'http://localhost',
+ 'http://localhost.example.com/beans' => 'http://localhost',
+ 'http://localhost@example.com/beans' => 'http://localhost',
+ ];
+
+ $this->asEditor();
+ foreach ($expectedByRedirect as $url => $expected) {
+ $req = $this->patch("/preferences/change-view/bookshelf", [
+ 'view' => 'grid',
+ '_return' => $url,
+ ]);
+ $req->assertRedirect($expected);
+ }
+ }
+
public function test_update_code_language_favourite()
{
$editor = $this->users->editor();
From 5f5fea7c83992cecca61b4aabfc1e07053dfa4c4 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Thu, 12 Mar 2026 10:52:12 +0000
Subject: [PATCH 05/85] Deps: Bumped PHP packages before release
---
composer.lock | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/composer.lock b/composer.lock
index 8ee6233b8..d8ea00662 100644
--- a/composer.lock
+++ b/composer.lock
@@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.372.2",
+ "version": "3.373.0",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "d1885c8c5db03c2a23121e6df58ef5693df41b95"
+ "reference": "fb74a2dca7ae2363e929c5cea33a4a4db0d22690"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d1885c8c5db03c2a23121e6df58ef5693df41b95",
- "reference": "d1885c8c5db03c2a23121e6df58ef5693df41b95",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/fb74a2dca7ae2363e929c5cea33a4a4db0d22690",
+ "reference": "fb74a2dca7ae2363e929c5cea33a4a4db0d22690",
"shasum": ""
},
"require": {
@@ -153,9 +153,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.372.2"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.373.0"
},
- "time": "2026-03-09T18:21:50+00:00"
+ "time": "2026-03-11T18:33:36+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -1801,16 +1801,16 @@
},
{
"name": "laravel/framework",
- "version": "v12.54.0",
+ "version": "v12.54.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "e908e117421bcade301b174aba2d3e8cc1e1f213"
+ "reference": "325497463e7599cd14224c422c6e5dd2fe832868"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/e908e117421bcade301b174aba2d3e8cc1e1f213",
- "reference": "e908e117421bcade301b174aba2d3e8cc1e1f213",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/325497463e7599cd14224c422c6e5dd2fe832868",
+ "reference": "325497463e7599cd14224c422c6e5dd2fe832868",
"shasum": ""
},
"require": {
@@ -2019,7 +2019,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-03-10T15:30:40+00:00"
+ "time": "2026-03-10T20:25:56+00:00"
},
{
"name": "laravel/prompts",
@@ -3361,16 +3361,16 @@
},
{
"name": "nesbot/carbon",
- "version": "3.11.1",
+ "version": "3.11.3",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
- "reference": "f438fcc98f92babee98381d399c65336f3a3827f"
+ "reference": "6a7e652845bb018c668220c2a545aded8594fbbf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/f438fcc98f92babee98381d399c65336f3a3827f",
- "reference": "f438fcc98f92babee98381d399c65336f3a3827f",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6a7e652845bb018c668220c2a545aded8594fbbf",
+ "reference": "6a7e652845bb018c668220c2a545aded8594fbbf",
"shasum": ""
},
"require": {
@@ -3462,7 +3462,7 @@
"type": "tidelift"
}
],
- "time": "2026-01-29T09:26:29+00:00"
+ "time": "2026-03-11T17:23:39+00:00"
},
{
"name": "nette/schema",
From 60a3b0c0acb9685fb303b25f503b690cb2083213 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Thu, 12 Mar 2026 17:04:36 +0000
Subject: [PATCH 06/85] API examples: Updated books-read to include shelf info
---
dev/api/responses/books-read.json | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/dev/api/responses/books-read.json b/dev/api/responses/books-read.json
index 582744f99..0c8ff473d 100644
--- a/dev/api/responses/books-read.json
+++ b/dev/api/responses/books-read.json
@@ -79,5 +79,17 @@
"path": "/uploads/images/cover_book/2020-01/sjovall_m117hUWMu40.jpg",
"type": "cover_book",
"uploaded_to": 16
- }
+ },
+ "shelves": [
+ {
+ "id": 1,
+ "name": "Great reads",
+ "slug": "great-reads"
+ },
+ {
+ "id": 5,
+ "name": "Personal Books",
+ "slug": "personal-books"
+ }
+ ]
}
\ No newline at end of file
From f4c9d2b0492d5a2dc3de052352f7a2e90d150f3f Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Fri, 13 Mar 2026 13:35:28 +0000
Subject: [PATCH 07/85] Exports: Fixed scope of pages in chapter MD export
Added tests to cover children of all MD exports
---
app/Exports/ExportFormatter.php | 2 +-
tests/Exports/MarkdownExportTest.php | 46 ++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/app/Exports/ExportFormatter.php b/app/Exports/ExportFormatter.php
index ad489aba1..c5973eace 100644
--- a/app/Exports/ExportFormatter.php
+++ b/app/Exports/ExportFormatter.php
@@ -323,7 +323,7 @@ class ExportFormatter
$text .= $description . "\n\n";
}
- foreach ($chapter->pages as $page) {
+ foreach ($chapter->getVisiblePages() as $page) {
$text .= $this->pageToMarkdown($page) . "\n\n";
}
diff --git a/tests/Exports/MarkdownExportTest.php b/tests/Exports/MarkdownExportTest.php
index 6bf585d59..09928ced2 100644
--- a/tests/Exports/MarkdownExportTest.php
+++ b/tests/Exports/MarkdownExportTest.php
@@ -56,6 +56,20 @@ class MarkdownExportTest extends TestCase
$resp->assertSee('My **chapter** description');
}
+ public function test_chapter_markdown_export_pages_are_permission_controlled()
+ {
+ $chapter = $this->entities->chapterHasPages();
+ $page = $chapter->pages()->first();
+ $page->name = 'MyPageWhichShouldNotBeFound';
+ $page->save();
+ $this->permissions->disableEntityInheritedPermissions($page);
+
+ $resp = $this->asEditor()->get($chapter->getUrl('/export/markdown'));
+
+ $resp->assertSee('# ' . $chapter->name);
+ $resp->assertDontSee('MyPageWhichShouldNotBeFound');
+ }
+
public function test_book_markdown_export()
{
$book = Book::query()->whereHas('pages')->whereHas('chapters')->first();
@@ -76,6 +90,38 @@ class MarkdownExportTest extends TestCase
$resp->assertSee('My **chapter** description');
}
+ public function test_book_markdown_export_chapters_are_permission_controlled()
+ {
+ $book = $this->entities->bookHasChaptersAndPages();
+ $chapter = $book->chapters()->first();
+ $page = $chapter->pages()->first();
+ $page->name = 'MyPageWhichShouldNotBeFound';
+ $page->save();
+ $chapter->name = 'MyChapterWhichShouldNotBeFound';
+ $chapter->save();
+ $this->permissions->disableEntityInheritedPermissions($chapter);
+
+ $resp = $this->asEditor()->get($book->getUrl('/export/markdown'));
+
+ $resp->assertSee('# ' . $book->name);
+ $resp->assertDontSee('MyChapterWhichShouldNotBeFound');
+ $resp->assertDontSee('MyPageWhichShouldNotBeFound');
+ }
+
+ public function test_book_markdown_export_direct_pages_are_permission_controlled()
+ {
+ $book = $this->entities->bookHasChaptersAndPages();
+ $page = $book->directPages()->first();
+ $page->name = 'MyPageWhichShouldNotBeFound';
+ $page->save();
+ $this->permissions->disableEntityInheritedPermissions($page);
+
+ $resp = $this->asEditor()->get($book->getUrl('/export/markdown'));
+
+ $resp->assertSee('# ' . $book->name);
+ $resp->assertDontSee('MyPageWhichShouldNotBeFound');
+ }
+
public function test_book_markdown_export_concats_immediate_pages_with_newlines()
{
/** @var Book $book */
From 7cbfd72920faf7e53c5b681119ac88adada811c3 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 15 Mar 2026 12:58:05 +0000
Subject: [PATCH 08/85] Merge pull request #6007 from
BookStackApp/l10n_development
Updated translations with latest Crowdin changes
---
lang/ar/settings.php | 2 +-
lang/bg/settings.php | 2 +-
lang/bn/settings.php | 2 +-
lang/bs/settings.php | 2 +-
lang/ca/settings.php | 2 +-
lang/cs/errors.php | 2 +-
lang/cs/settings.php | 2 +-
lang/cy/settings.php | 2 +-
lang/da/settings.php | 2 +-
lang/de/settings.php | 2 +-
lang/de_informal/settings.php | 2 +-
lang/el/settings.php | 2 +-
lang/es/errors.php | 2 +-
lang/es/settings.php | 2 +-
lang/es_AR/errors.php | 2 +-
lang/es_AR/settings.php | 2 +-
lang/et/errors.php | 2 +-
lang/et/settings.php | 2 +-
lang/eu/settings.php | 2 +-
lang/fa/settings.php | 2 +-
lang/fi/settings.php | 2 +-
lang/fr/errors.php | 4 ++--
lang/fr/settings.php | 2 +-
lang/he/settings.php | 2 +-
lang/hr/settings.php | 2 +-
lang/hu/settings.php | 2 +-
lang/id/settings.php | 2 +-
lang/is/settings.php | 2 +-
lang/it/errors.php | 2 +-
lang/it/settings.php | 2 +-
lang/ja/errors.php | 2 +-
lang/ja/settings.php | 2 +-
lang/ka/settings.php | 2 +-
lang/ko/settings.php | 2 +-
lang/ku/settings.php | 2 +-
lang/lt/settings.php | 2 +-
lang/lv/settings.php | 2 +-
lang/nb/settings.php | 2 +-
lang/ne/settings.php | 2 +-
lang/nl/settings.php | 2 +-
lang/nn/settings.php | 2 +-
lang/pl/settings.php | 2 +-
lang/pt/settings.php | 2 +-
lang/pt_BR/errors.php | 4 ++--
lang/pt_BR/notifications.php | 4 ++--
lang/pt_BR/preferences.php | 2 +-
lang/pt_BR/settings.php | 6 +++---
lang/pt_BR/validation.php | 2 +-
lang/ro/settings.php | 2 +-
lang/ru/entities.php | 38 +++++++++++++++++------------------
lang/ru/notifications.php | 2 +-
lang/ru/settings.php | 6 +++---
lang/sk/settings.php | 2 +-
lang/sl/settings.php | 2 +-
lang/sq/settings.php | 2 +-
lang/sr/settings.php | 2 +-
lang/sv/entities.php | 2 +-
lang/sv/preferences.php | 2 +-
lang/sv/settings.php | 2 +-
lang/tk/settings.php | 2 +-
lang/tr/settings.php | 2 +-
lang/uk/settings.php | 2 +-
lang/uz/settings.php | 2 +-
lang/vi/settings.php | 2 +-
lang/zh_CN/editor.php | 2 +-
lang/zh_CN/entities.php | 10 ++++-----
lang/zh_CN/errors.php | 4 ++--
lang/zh_CN/notifications.php | 4 ++--
lang/zh_CN/preferences.php | 2 +-
lang/zh_CN/settings.php | 12 +++++------
lang/zh_CN/validation.php | 2 +-
lang/zh_TW/settings.php | 2 +-
72 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/lang/ar/settings.php b/lang/ar/settings.php
index dc95ac846..3191bbe3a 100644
--- a/lang/ar/settings.php
+++ b/lang/ar/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'الفصول الأولى',
'sort_rule_op_chapters_last' => 'الفصول الأخيرة',
'sorting_page_limits' => 'حدود العرض لكل صفحة',
- 'sorting_page_limits_desc' => 'تعيين عدد العناصر لإظهار كل صفحة في قوائم مختلفة داخل النظام. عادةً ما يكون الرقم الأقل هو الأكثر أداء، بينما وضع رقم أعلى يغني عن النقر على صفحات متعددة. يوصى باستخدام مضاعفات رقم ٣ (18 و 24 و 30 و إلخ...).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'الصيانة',
diff --git a/lang/bg/settings.php b/lang/bg/settings.php
index ae770c559..a1297e446 100644
--- a/lang/bg/settings.php
+++ b/lang/bg/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Поддръжка',
diff --git a/lang/bn/settings.php b/lang/bn/settings.php
index 6d0f4ab88..94ad059d4 100644
--- a/lang/bn/settings.php
+++ b/lang/bn/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/bs/settings.php b/lang/bs/settings.php
index c68605fe1..c4d1eb136 100644
--- a/lang/bs/settings.php
+++ b/lang/bs/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/ca/settings.php b/lang/ca/settings.php
index 352291fe5..a890b9809 100644
--- a/lang/ca/settings.php
+++ b/lang/ca/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Capítols a l\'inici',
'sort_rule_op_chapters_last' => 'Capítols al final',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Manteniment',
diff --git a/lang/cs/errors.php b/lang/cs/errors.php
index 176320642..5d086456b 100644
--- a/lang/cs/errors.php
+++ b/lang/cs/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Poskytnutý Token Secret neodpovídá použitému API tokenu',
'api_user_no_api_permission' => 'Vlastník použitého API tokenu nemá oprávnění provádět API volání',
'api_user_token_expired' => 'Platnost autorizačního tokenu vypršela',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Při používání API s ověřováním pomocí souborů cookie jsou povoleny pouze požadavky GET',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Při posílání testovacího e-mailu nastala chyba:',
diff --git a/lang/cs/settings.php b/lang/cs/settings.php
index 73ba6bfb0..ef25f1a20 100644
--- a/lang/cs/settings.php
+++ b/lang/cs/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Kapitoly jako první',
'sort_rule_op_chapters_last' => 'Kapitoly jako poslední',
'sorting_page_limits' => 'Počet zobrazených položek na stránce',
- 'sorting_page_limits_desc' => 'Nastavte, kolik položek se má zobrazit na stránce v různých seznamech na webu. Obvykle bude nižší počet výkonnější, zatímco vyšší počet eliminuje nutnost proklikávat se několika stránkami. Doporučuje se použít sudý násobek čísla 3 (18, 24, 30 atd.).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Údržba',
diff --git a/lang/cy/settings.php b/lang/cy/settings.php
index 29e86e28b..f4fbf0bba 100644
--- a/lang/cy/settings.php
+++ b/lang/cy/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Cynnal',
diff --git a/lang/da/settings.php b/lang/da/settings.php
index 2f161ed4f..cd869c62f 100644
--- a/lang/da/settings.php
+++ b/lang/da/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Kapitler først',
'sort_rule_op_chapters_last' => 'De sidste kapitler',
'sorting_page_limits' => 'Visningsgrænser pr. side',
- 'sorting_page_limits_desc' => 'Angiv, hvor mange elementer der skal vises pr. side i forskellige lister i systemet. Typisk vil et lavere beløb være mere effektivt, mens et højere beløb undgår behovet for at klikke sig igennem flere sider. Det anbefales at bruge et lige multiplum af 3 (18, 24, 30 osv.).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Vedligeholdelse',
diff --git a/lang/de/settings.php b/lang/de/settings.php
index a87408995..e57ac52c4 100644
--- a/lang/de/settings.php
+++ b/lang/de/settings.php
@@ -105,7 +105,7 @@ Hinweis: Benutzer können ihre E-Mail-Adresse nach erfolgreicher Registrierung
'sort_rule_op_chapters_first' => 'Kapitel zuerst',
'sort_rule_op_chapters_last' => 'Kapitel zuletzt',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Wartung',
diff --git a/lang/de_informal/settings.php b/lang/de_informal/settings.php
index ab9a075a0..fa175187b 100644
--- a/lang/de_informal/settings.php
+++ b/lang/de_informal/settings.php
@@ -105,7 +105,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung
'sort_rule_op_chapters_first' => 'Kapitel zuerst',
'sort_rule_op_chapters_last' => 'Kapitel zuletzt',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Wartung',
diff --git a/lang/el/settings.php b/lang/el/settings.php
index 67461604e..6ec5c4fdd 100644
--- a/lang/el/settings.php
+++ b/lang/el/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Συντήρηση',
diff --git a/lang/es/errors.php b/lang/es/errors.php
index 76581257e..25f82a5d8 100644
--- a/lang/es/errors.php
+++ b/lang/es/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'El secreto proporcionado para el token API usado es incorrecto',
'api_user_no_api_permission' => 'El propietario del token API usado no tiene permiso para hacer llamadas API',
'api_user_token_expired' => 'El token de autorización usado ha caducado',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Sólo se permiten peticiones GET cuando se utiliza el API con autenticación basada en cookies',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
diff --git a/lang/es/settings.php b/lang/es/settings.php
index 1a9927c8e..bfd3ce1cf 100644
--- a/lang/es/settings.php
+++ b/lang/es/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Capítulos al inicio',
'sort_rule_op_chapters_last' => 'Capítulos al final',
'sorting_page_limits' => 'Límites de visualización por página',
- 'sorting_page_limits_desc' => 'Establecer cuántos elementos a mostrar por página en varias listas dentro del sistema. Normalmente una cantidad más baja rendirá mejor, mientras que una cantidad más alta evita la necesidad de hacer clic a través de varias páginas. Se recomienda utilizar un múltiplo par de 3 (18, 24, 30, etc).',
+ 'sorting_page_limits_desc' => 'Establecer cuántos elementos a mostrar por página en varias listas dentro del sistema. Normalmente una cantidad más baja rendirá mejor, mientras que una cantidad más alta evita la necesidad de hacer clic a través de varias páginas. Se recomienda utilizar un múltiplo de 6.',
// Maintenance settings
'maint' => 'Mantenimiento',
diff --git a/lang/es_AR/errors.php b/lang/es_AR/errors.php
index 5e052c51d..6f6b25bcf 100644
--- a/lang/es_AR/errors.php
+++ b/lang/es_AR/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'El secreto proporcionado para el token API usado es incorrecto',
'api_user_no_api_permission' => 'El propietario del token API usado no tiene permiso para hacer llamadas API',
'api_user_token_expired' => 'El token de autorización usado ha caducado',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Sólo se permiten peticiones GET cuando se utiliza el API con autenticación basada en cookies',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Error al enviar un email de prueba:',
diff --git a/lang/es_AR/settings.php b/lang/es_AR/settings.php
index 3eb41d2cc..90f43a6f2 100644
--- a/lang/es_AR/settings.php
+++ b/lang/es_AR/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Capítulos al inicio',
'sort_rule_op_chapters_last' => 'Capítulos al final',
'sorting_page_limits' => 'Límites de visualización por página',
- 'sorting_page_limits_desc' => 'Establecer cuántos elementos a mostrar por página en varias listas dentro del sistema. Normalmente una cantidad más baja rendirá mejor, mientras que una cantidad más alta evita la necesidad de hacer clic a través de varias páginas. Se recomienda utilizar un múltiplo par de 3 (18, 24, 30, etc).',
+ 'sorting_page_limits_desc' => 'Establecer cuántos elementos a mostrar por página en varias listas dentro del sistema. Normalmente una cantidad más baja rendirá mejor, mientras que una cantidad más alta evita la necesidad de hacer clic a través de varias páginas. Se recomienda utilizar un múltiplo de 6.',
// Maintenance settings
'maint' => 'Mantenimiento',
diff --git a/lang/et/errors.php b/lang/et/errors.php
index c9b9fbb4e..78a9d8c47 100644
--- a/lang/et/errors.php
+++ b/lang/et/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'API tunnusele lisatud salajane võti ei ole korrektne',
'api_user_no_api_permission' => 'Selle API tunnuse omanikul ei ole õigust API päringuid teha',
'api_user_token_expired' => 'Volitustunnus on aegunud',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Küpsistega autentimisel on API kasutamisel lubatud ainult GET päringud',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Test e-kirja saatmisel tekkis viga:',
diff --git a/lang/et/settings.php b/lang/et/settings.php
index fbd9d9c7e..bc5a7794e 100644
--- a/lang/et/settings.php
+++ b/lang/et/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Peatükid eespool',
'sort_rule_op_chapters_last' => 'Peatükid tagapool',
'sorting_page_limits' => 'Leheküljepõhised kuvalimiidid',
- 'sorting_page_limits_desc' => 'Seadista, mitut objekti erinevates loendites ühel leheküljel kuvada. Väiksem väärtus tähendab reeglina paremat jõudlust, samas kui suurem väärtus vähendab vajadust mitut lehekülge läbi klikkida. Soovituslik on kasutada 3-ga jaguvat väärtust (18, 24, 30 jne).',
+ 'sorting_page_limits_desc' => 'Vali, mitu objekti erinevates nimekirjades ühel lehel kuvada. Madalam väärtus tähendab reeglina paremat jõudlust, samas kui kõrgem väärtus väldib vajadust mitmeid lehti läbi klikkida. Soovituslik on kasutada 6-ga jaguvat väärtust.',
// Maintenance settings
'maint' => 'Hooldus',
diff --git a/lang/eu/settings.php b/lang/eu/settings.php
index 9a9227b83..0f764dccb 100644
--- a/lang/eu/settings.php
+++ b/lang/eu/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Mantentze-lanak',
diff --git a/lang/fa/settings.php b/lang/fa/settings.php
index abbfce470..2fa115118 100644
--- a/lang/fa/settings.php
+++ b/lang/fa/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'ابتدا فصلها',
'sort_rule_op_chapters_last' => 'فصلها در آخر',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'نگهداری',
diff --git a/lang/fi/settings.php b/lang/fi/settings.php
index 499122fe3..adc47fe2d 100644
--- a/lang/fi/settings.php
+++ b/lang/fi/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Huolto',
diff --git a/lang/fr/errors.php b/lang/fr/errors.php
index a5a3614c2..b74fb5465 100644
--- a/lang/fr/errors.php
+++ b/lang/fr/errors.php
@@ -13,7 +13,7 @@ return [
'auth_pre_register_theme_prevention' => 'Le compte utilisateur n\'a pas pu être enregistré avec les informations fournies',
'email_already_confirmed' => 'Cet e-mail a déjà été validé, vous pouvez vous connecter.',
'email_confirmation_invalid' => 'Cette confirmation est invalide. Veuillez essayer de vous inscrire à nouveau.',
- 'email_confirmation_expired' => 'Le jeton de confirmation est périmé. Un nouvel e-mail vous a été envoyé.',
+ 'email_confirmation_expired' => 'Le jeton de confirmation a expiré. Un nouvel e-mail vous a été envoyé.',
'email_confirmation_awaiting' => 'L\'adresse e-mail du compte utilisé doit être confirmée',
'ldap_fail_anonymous' => 'L\'accès LDAP anonyme n\'a pas abouti',
'ldap_fail_authed' => 'L\'accès LDAP n\'a pas abouti avec cet utilisateur et ce mot de passe',
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Le secret fourni pour le jeton d\'API utilisé est incorrect',
'api_user_no_api_permission' => 'Le propriétaire du jeton API utilisé n\'a pas la permission de passer des requêtes API',
'api_user_token_expired' => 'Le jeton d\'autorisation utilisé a expiré',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Seules les requêtes GET sont autorisées lors de l’utilisation de l’API avec une authentification basée sur les cookies',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erreur émise lors de l\'envoi d\'un e-mail de test :',
diff --git a/lang/fr/settings.php b/lang/fr/settings.php
index 317e777b9..7b0987a48 100644
--- a/lang/fr/settings.php
+++ b/lang/fr/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapitres en premier',
'sort_rule_op_chapters_last' => 'Chapitres en dernier',
'sorting_page_limits' => 'Limite d\'affichage par page',
- 'sorting_page_limits_desc' => 'Définissez le nombre d’éléments à afficher par page dans les différentes listes du système. En général, un nombre plus faible offre de meilleures performances, tandis qu’un nombre plus élevé réduit le besoin de naviguer entre plusieurs pages. Il est recommandé d’utiliser un multiple pair de 3 (18, 24, 30, etc.).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/he/settings.php b/lang/he/settings.php
index 0b5034475..46150081a 100644
--- a/lang/he/settings.php
+++ b/lang/he/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'תחזוקה',
diff --git a/lang/hr/settings.php b/lang/hr/settings.php
index 6465d0ea7..0692c8d7a 100644
--- a/lang/hr/settings.php
+++ b/lang/hr/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Održavanje',
diff --git a/lang/hu/settings.php b/lang/hu/settings.php
index c6810ed68..53b1cdcc4 100644
--- a/lang/hu/settings.php
+++ b/lang/hu/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Karbantartás',
diff --git a/lang/id/settings.php b/lang/id/settings.php
index cc9426683..8bdd99e68 100644
--- a/lang/id/settings.php
+++ b/lang/id/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Bab di Urutan Pertama',
'sort_rule_op_chapters_last' => 'Bab di Urutan Terakhir',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Pemeliharaan',
diff --git a/lang/is/settings.php b/lang/is/settings.php
index 5699c88d6..b1f21ac10 100644
--- a/lang/is/settings.php
+++ b/lang/is/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Viðhald',
diff --git a/lang/it/errors.php b/lang/it/errors.php
index 99700088b..1d6c71636 100644
--- a/lang/it/errors.php
+++ b/lang/it/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Il token segreto fornito per il token API utilizzato non è corretto',
'api_user_no_api_permission' => 'Il proprietario del token API utilizzato non ha il permesso di effettuare chiamate API',
'api_user_token_expired' => 'Il token di autorizzazione utilizzato è scaduto',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Solo le richieste GET sono consentite quando si utilizza l\'API con autenticazione basata sui cookie',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Si è verificato un errore durante l\'invio di una e-mail di prova:',
diff --git a/lang/it/settings.php b/lang/it/settings.php
index 88c6a4c11..2b5819b2a 100644
--- a/lang/it/settings.php
+++ b/lang/it/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Capitoli Prima',
'sort_rule_op_chapters_last' => 'Capitoli dopo',
'sorting_page_limits' => 'Limiti Visualizzazione Per Pagina',
- 'sorting_page_limits_desc' => 'Imposta il numero di elementi da visualizzare per pagina nei vari elenchi all\'interno del sistema. In genere, un numero inferiore garantisce prestazioni migliori, mentre un numero più elevato evita la necessità di cliccare su più pagine. Si consiglia di utilizzare un multiplo pari di 3 (18, 24, 30, ecc...).',
+ 'sorting_page_limits_desc' => 'Imposta il numero di elementi da visualizzare per pagina nei vari elenchi del sistema. In genere, un numero inferiore garantisce prestazioni migliori, mentre un numero maggiore evita di dover sfogliare più pagine. Si consiglia di utilizzare un multiplo di 6.',
// Maintenance settings
'maint' => 'Manutenzione',
diff --git a/lang/ja/errors.php b/lang/ja/errors.php
index 7ce8db111..0c1c8d84a 100644
--- a/lang/ja/errors.php
+++ b/lang/ja/errors.php
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => '利用されたAPIトークンに対して提供されたシークレットが正しくありません',
'api_user_no_api_permission' => '使用されているAPIトークンの所有者には、API呼び出しを行う権限がありません',
'api_user_token_expired' => '認証トークンが期限切れです。',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Cookie ベースの認証で API を使用する場合、GET リクエストのみが許可されます',
// Settings & Maintenance
'maintenance_test_email_failure' => 'テストメール送信時にエラーが発生しました:',
diff --git a/lang/ja/settings.php b/lang/ja/settings.php
index 53b14233e..717a4c10f 100644
--- a/lang/ja/settings.php
+++ b/lang/ja/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'チャプタを最初に',
'sort_rule_op_chapters_last' => 'チャプタを最後に',
'sorting_page_limits' => 'ページング表示制限',
- 'sorting_page_limits_desc' => 'システム内の各種リストで1ページに表示するアイテム数を設定します。 通常、少ない数に設定するとパフォーマンスが向上し、多い数に設定するとページの移動操作が少なくなります。 3の倍数(18、24、30など)を使用することをお勧めします。',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'メンテナンス',
diff --git a/lang/ka/settings.php b/lang/ka/settings.php
index c68605fe1..c4d1eb136 100644
--- a/lang/ka/settings.php
+++ b/lang/ka/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/ko/settings.php b/lang/ko/settings.php
index 0488bfe14..97af673c6 100644
--- a/lang/ko/settings.php
+++ b/lang/ko/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => '챕터 우선 정렬',
'sort_rule_op_chapters_last' => '챕터 나중 정렬',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => '유지관리',
diff --git a/lang/ku/settings.php b/lang/ku/settings.php
index c68605fe1..c4d1eb136 100644
--- a/lang/ku/settings.php
+++ b/lang/ku/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/lt/settings.php b/lang/lt/settings.php
index bcc7c82bd..f797e567e 100644
--- a/lang/lt/settings.php
+++ b/lang/lt/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Priežiūra',
diff --git a/lang/lv/settings.php b/lang/lv/settings.php
index fb5cf13dd..9dc6bf402 100644
--- a/lang/lv/settings.php
+++ b/lang/lv/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Nodaļas pirmās',
'sort_rule_op_chapters_last' => 'Nodaļas pēdējās',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Apkope',
diff --git a/lang/nb/settings.php b/lang/nb/settings.php
index 61b1c3367..5fcaaaca6 100644
--- a/lang/nb/settings.php
+++ b/lang/nb/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Kapitler først',
'sort_rule_op_chapters_last' => 'Kapitler sist',
'sorting_page_limits' => 'Visningsgrenser for hver side',
- 'sorting_page_limits_desc' => 'Angi hvor mange elementer som skal vises på hver side i ulike lister i systemet. Et lavere antall vil vanligvis gi bedre ytelse, mens et høyere antall reduserer behovet for å bla gjennom mange sider. Det er anbefalt å bruke en multiplikasjon av 3 som gir partall (18, 24, 30 osv.).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Vedlikehold',
diff --git a/lang/ne/settings.php b/lang/ne/settings.php
index 37e59978e..dbc7d8e9f 100644
--- a/lang/ne/settings.php
+++ b/lang/ne/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'पहिले अध्यायहरू',
'sort_rule_op_chapters_last' => 'अन्त्यमा अध्यायहरू',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'सम्भार',
diff --git a/lang/nl/settings.php b/lang/nl/settings.php
index 63805f498..c8d071119 100644
--- a/lang/nl/settings.php
+++ b/lang/nl/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Hoofdstukken Eerst',
'sort_rule_op_chapters_last' => 'Hoofdstukken Laatst',
'sorting_page_limits' => 'Weergavelimiet Per Pagina',
- 'sorting_page_limits_desc' => 'Stel in hoeveel items er op een pagina worden laten zien in de verschillende lijstweergaves. Een lager aantal verbeterd de snelheid, een hoger aantal verminderd het doorklikken door pagina\'s. Een even veelvoud van 3 (18, 24, 30, etc...) wordt aanbevolen.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Onderhoud',
diff --git a/lang/nn/settings.php b/lang/nn/settings.php
index 6d2259bd8..e4b6e6af9 100644
--- a/lang/nn/settings.php
+++ b/lang/nn/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Vedlikehold',
diff --git a/lang/pl/settings.php b/lang/pl/settings.php
index 5954b005f..775d4f25d 100644
--- a/lang/pl/settings.php
+++ b/lang/pl/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Rozdziały na początku',
'sort_rule_op_chapters_last' => 'Rozdziały na końcu',
'sorting_page_limits' => 'Limity wyświetlania per strona',
- 'sorting_page_limits_desc' => 'Ustaw ile elementów pokazywać per strona w różnych listach w systemie. Zazwyczaj mniejsza ilość będzie bardziej wydajna, podczas gdy większa ilość unika konieczności przeglądania wielu stron. Zaleca się stosowanie parzystej wielokrotności 3 (18, 24, 30 itp...).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Konserwacja',
diff --git a/lang/pt/settings.php b/lang/pt/settings.php
index c2179e640..a59335b7c 100644
--- a/lang/pt/settings.php
+++ b/lang/pt/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Manutenção',
diff --git a/lang/pt_BR/errors.php b/lang/pt_BR/errors.php
index 4d711076f..328715433 100644
--- a/lang/pt_BR/errors.php
+++ b/lang/pt_BR/errors.php
@@ -110,7 +110,7 @@ return [
'import_zip_cant_read' => 'Não foi possível ler o arquivo ZIP.',
'import_zip_cant_decode_data' => 'Não foi possível encontrar e decodificar o conteúdo ZIP data.json.',
'import_zip_no_data' => 'Os dados do arquivo ZIP não têm o conteúdo esperado livro, capítulo ou página.',
- 'import_zip_data_too_large' => 'ZIP data.json content exceeds the configured application maximum upload size.',
+ 'import_zip_data_too_large' => 'O conteúdo ZIP data.json excede o tamanho máximo de upload configurado para a aplicação.',
'import_validation_failed' => 'Falhou na validação da importação do ZIP com erros:',
'import_zip_failed_notification' => 'Falhou ao importar arquivo ZIP.',
'import_perms_books' => 'Você não tem as permissões necessárias para criar livros.',
@@ -126,7 +126,7 @@ return [
'api_incorrect_token_secret' => 'O segredo fornecido para o código de API usado está incorreto',
'api_user_no_api_permission' => 'O proprietário do código de API utilizado não tem permissão para fazer requisições de API',
'api_user_token_expired' => 'O código de autenticação expirou',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Somente solicitações GET são permitidas ao usar a API com autenticação baseada em cookies',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Erro encontrado ao enviar uma mensagem eletrônica de teste:',
diff --git a/lang/pt_BR/notifications.php b/lang/pt_BR/notifications.php
index 8c98467c8..7f6c100b1 100644
--- a/lang/pt_BR/notifications.php
+++ b/lang/pt_BR/notifications.php
@@ -11,8 +11,8 @@ return [
'updated_page_subject' => 'Página atualizada: :pageName',
'updated_page_intro' => 'Uma página foi atualizada em :appName:',
'updated_page_debounce' => 'Para prevenir notificações em massa, por enquanto notificações não serão enviadas para você para próximas edições nessa página pelo mesmo editor.',
- 'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
- 'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
+ 'comment_mention_subject' => 'Você foi mencionado em um comentário na página: :pageName',
+ 'comment_mention_intro' => 'Você foi mencionado em um comentário sobre :appName:',
'detail_page_name' => 'Nome da Página:',
'detail_page_path' => 'Caminho da Página:',
diff --git a/lang/pt_BR/preferences.php b/lang/pt_BR/preferences.php
index d2b7fc540..a92d971c3 100644
--- a/lang/pt_BR/preferences.php
+++ b/lang/pt_BR/preferences.php
@@ -23,7 +23,7 @@ return [
'notifications_desc' => 'Controle as notificações por e-mail que você recebe quando uma determinada atividade é executada no sistema.',
'notifications_opt_own_page_changes' => 'Notificar quando houver alterações em páginas que eu possuo',
'notifications_opt_own_page_comments' => 'Notificar comentários nas páginas que eu possuo',
- 'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
+ 'notifications_opt_comment_mentions' => 'Notificar quando eu for mencionado em um comentário',
'notifications_opt_comment_replies' => 'Notificar ao responder aos meus comentários',
'notifications_save' => 'Salvar Preferências',
'notifications_update_success' => 'Preferências de notificação foram atualizadas!',
diff --git a/lang/pt_BR/settings.php b/lang/pt_BR/settings.php
index 53947a36d..97b434727 100644
--- a/lang/pt_BR/settings.php
+++ b/lang/pt_BR/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Capítulos Primeiro',
'sort_rule_op_chapters_last' => 'Capítulos por Último',
'sorting_page_limits' => 'Limites de exibição por página',
- 'sorting_page_limits_desc' => 'Defina quantos itens serão exibidos por página em diferentes listas do sistema. Normalmente, um número menor proporciona melhor desempenho, enquanto um número maior evita a necessidade de clicar em várias páginas. É recomendado o uso de um múltiplo par de 3 (18, 24, 30, etc.).',
+ 'sorting_page_limits_desc' => 'Defina quantos itens mostrar por página em várias listas no sistema. Normalmente, uma quantidade menor será mais eficiente, enquanto uma quantidade maior evita a necessidade de clicar em várias páginas. Recomenda-se usar um múltiplo de 6.',
// Maintenance settings
'maint' => 'Manutenção',
@@ -197,13 +197,13 @@ return [
'role_import_content' => 'Importar conteúdo',
'role_editor_change' => 'Alterar página de edição',
'role_notifications' => 'Receber e gerenciar notificações',
- 'role_permission_note_users_and_roles' => 'These permissions will technically also provide visibility & searching of users & roles in the system.',
+ 'role_permission_note_users_and_roles' => 'Essas permissões tecnicamente também fornecerão visibilidade e busca de usuários e perfis no sistema.',
'role_asset' => 'Permissões de Ativos',
'roles_system_warning' => 'Esteja ciente de que o acesso a qualquer uma das três permissões acima pode permitir que um usuário altere seus próprios privilégios ou privilégios de outros usuários no sistema. Apenas atribua perfis com essas permissões para usuários confiáveis.',
'role_asset_desc' => 'Essas permissões controlam o acesso padrão para os ativos dentro do sistema. Permissões em Livros, Capítulos e Páginas serão sobrescritas por essas permissões.',
'role_asset_admins' => 'Administradores recebem automaticamente acesso a todo o conteúdo, mas essas opções podem mostrar ou ocultar as opções da Interface de Usuário.',
'role_asset_image_view_note' => 'Isso está relacionado à visibilidade no gerenciador de imagens. O acesso real dos arquivos de imagem carregados dependerá da opção de armazenamento de imagem do sistema.',
- 'role_asset_users_note' => 'These permissions will technically also provide visibility & searching of users in the system.',
+ 'role_asset_users_note' => 'Essas permissões tecnicamente também fornecerão visibilidade e busca de usuários do sistema.',
'role_all' => 'Todos',
'role_own' => 'Próprio',
'role_controlled_by_asset' => 'Controlado pelos ativos nos quais o upload foi realizado',
diff --git a/lang/pt_BR/validation.php b/lang/pt_BR/validation.php
index e30ebc377..4d8139c4b 100644
--- a/lang/pt_BR/validation.php
+++ b/lang/pt_BR/validation.php
@@ -106,7 +106,7 @@ return [
'uploaded' => 'O arquivo não pôde ser carregado. O servidor pode não aceitar arquivos deste tamanho.',
'zip_file' => 'O :attribute precisa fazer referência a um arquivo do ZIP.',
- 'zip_file_size' => 'The file :attribute must not exceed :size MB.',
+ 'zip_file_size' => 'O arquivo :attribute não deve exceder :size MB.',
'zip_file_mime' => 'O :attribute precisa fazer referência a um arquivo do tipo :validTypes, encontrado :foundType.',
'zip_model_expected' => 'Objeto de dados esperado, mas ":type" encontrado.',
'zip_unique' => 'O :attribute deve ser único para o tipo de objeto dentro do ZIP.',
diff --git a/lang/ro/settings.php b/lang/ro/settings.php
index 02052ef3c..d65a8e071 100644
--- a/lang/ro/settings.php
+++ b/lang/ro/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Mentenanţă',
diff --git a/lang/ru/entities.php b/lang/ru/entities.php
index 50f061115..bf224c10a 100644
--- a/lang/ru/entities.php
+++ b/lang/ru/entities.php
@@ -39,21 +39,21 @@ return [
'export_pdf' => 'PDF файл',
'export_text' => 'Текстовый файл',
'export_md' => 'Файл Markdown',
- 'export_zip' => 'Portable ZIP',
+ 'export_zip' => 'Портативный ZIP',
'default_template' => 'Шаблон страницы по умолчанию',
'default_template_explain' => 'Назначить шаблон страницы, который будет использоваться в качестве содержимого по умолчанию для всех страниц, созданных в этом элементе. Имейте в виду, что это будет работать, только если создатель страницы имеет доступ к выбранной странице шаблона.',
'default_template_select' => 'Выберите страницу шаблона',
'import' => 'Импорт',
- 'import_validate' => 'Validate Import',
+ 'import_validate' => 'Проверка импорта',
'import_desc' => 'Импортировать книги, главы и страницы с помощью ZIP-файла, экспортированного из этого или другого источника. Выберите ZIP-файл, чтобы продолжить. После загрузки и проверки файла вы сможете настроить и подтвердить импорт в следующем окне.',
- 'import_zip_select' => 'Select ZIP file to upload',
- 'import_zip_validation_errors' => 'Errors were detected while validating the provided ZIP file:',
- 'import_pending' => 'Pending Imports',
- 'import_pending_none' => 'No imports have been started.',
- 'import_continue' => 'Continue Import',
+ 'import_zip_select' => 'Выберите ZIP файл для загрузки',
+ 'import_zip_validation_errors' => 'Были обнаружены ошибки при проверке предоставленного ZIP файла:',
+ 'import_pending' => 'Ожидается импорт',
+ 'import_pending_none' => 'Импорт не был запущен.',
+ 'import_continue' => 'Продолжить импорт',
'import_continue_desc' => 'Review the content due to be imported from the uploaded ZIP file. When ready, run the import to add its contents to this system. The uploaded ZIP import file will be automatically removed on successful import.',
'import_details' => 'Import Details',
- 'import_run' => 'Run Import',
+ 'import_run' => 'Запустить импорт',
'import_size' => ':size Import ZIP Size',
'import_uploaded_at' => 'Uploaded :relativeTime',
'import_uploaded_by' => 'Uploaded by',
@@ -61,7 +61,7 @@ return [
'import_location_desc' => 'Select a target location for your imported content. You\'ll need the relevant permissions to create within the location you choose.',
'import_delete_confirm' => 'Are you sure you want to delete this import?',
'import_delete_desc' => 'This will delete the uploaded import ZIP file, and cannot be undone.',
- 'import_errors' => 'Import Errors',
+ 'import_errors' => 'Ошибки импорта',
'import_errors_desc' => 'The follow errors occurred during the import attempt:',
'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
@@ -252,7 +252,7 @@ return [
'pages_edit_switch_to_markdown_stable' => 'Полное сохранение форматирования (HTML)',
'pages_edit_switch_to_wysiwyg' => 'Переключиться в WYSIWYG',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
- 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)',
+ 'pages_edit_switch_to_new_wysiwyg_desc' => '(В бета-тестировании)',
'pages_edit_set_changelog' => 'Задать список изменений',
'pages_edit_enter_changelog_desc' => 'Введите краткое описание внесенных изменений',
'pages_edit_enter_changelog' => 'Введите список изменений',
@@ -397,11 +397,11 @@ return [
'comment' => 'Комментарий',
'comments' => 'Комментарии',
'comment_add' => 'Комментировать',
- 'comment_none' => 'No comments to display',
+ 'comment_none' => 'Нет комментариев для отображения',
'comment_placeholder' => 'Оставить комментарий здесь',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
- 'comment_archived_count' => ':count Archived',
- 'comment_archived_threads' => 'Archived Threads',
+ 'comment_archived_count' => ':count архивировано',
+ 'comment_archived_threads' => 'Архивированные темы',
'comment_save' => 'Сохранить комментарий',
'comment_new' => 'Новый комментарий',
'comment_created' => 'прокомментировал :createDiff',
@@ -410,14 +410,14 @@ return [
'comment_deleted_success' => 'Комментарий удален',
'comment_created_success' => 'Комментарий добавлен',
'comment_updated_success' => 'Комментарий обновлен',
- 'comment_archive_success' => 'Comment archived',
- 'comment_unarchive_success' => 'Comment un-archived',
- 'comment_view' => 'View comment',
- 'comment_jump_to_thread' => 'Jump to thread',
+ 'comment_archive_success' => 'Комментарий заархивирован',
+ 'comment_unarchive_success' => 'Комментарий разархивирован',
+ 'comment_view' => 'Просмотреть комментарий',
+ 'comment_jump_to_thread' => 'Перейти к теме',
'comment_delete_confirm' => 'Удалить этот комментарий?',
'comment_in_reply_to' => 'В ответ на :commentId',
- 'comment_reference' => 'Reference',
- 'comment_reference_outdated' => '(Outdated)',
+ 'comment_reference' => 'Ссылка',
+ 'comment_reference_outdated' => '(Устаревшее)',
'comment_editor_explain' => 'Вот комментарии, которые были оставлены на этой странице. Комментарии могут быть добавлены и управляться при просмотре сохраненной страницы.',
// Revision
diff --git a/lang/ru/notifications.php b/lang/ru/notifications.php
index 289de42b6..96e853723 100644
--- a/lang/ru/notifications.php
+++ b/lang/ru/notifications.php
@@ -11,7 +11,7 @@ return [
'updated_page_subject' => 'Обновлена страница: :pageName',
'updated_page_intro' => 'Страница была обновлена в :appName:',
'updated_page_debounce' => 'Чтобы предотвратить массовые уведомления, в течение некоторого времени вы не будете получать уведомления о дальнейших правках этой страницы этим же редактором.',
- 'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
+ 'comment_mention_subject' => 'Вы были упомянуты в комментарии на странице: :pageName',
'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
'detail_page_name' => 'Имя страницы:',
diff --git a/lang/ru/settings.php b/lang/ru/settings.php
index 47839d520..76a2eebbf 100644
--- a/lang/ru/settings.php
+++ b/lang/ru/settings.php
@@ -75,7 +75,7 @@ return [
'reg_confirm_restrict_domain_placeholder' => 'Без ограничений',
// Sorting Settings
- 'sorting' => 'Lists & Sorting',
+ 'sorting' => 'Списки и сортировка',
'sorting_book_default' => 'Default Book Sort Rule',
'sorting_book_default_desc' => 'Выберите правило сортировки по умолчанию для новых книг. Это не повлияет на существующие книги, и может быть изменено для каждой книги отдельно.',
'sorting_rules' => 'Правила сортировки',
@@ -101,10 +101,10 @@ return [
'sort_rule_op_name_numeric' => 'По нумерации',
'sort_rule_op_created_date' => 'Created Date',
'sort_rule_op_updated_date' => 'Updated Date',
- 'sort_rule_op_chapters_first' => 'Chapters First',
+ 'sort_rule_op_chapters_first' => 'Главы в начале',
'sort_rule_op_chapters_last' => 'Главы в конце',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Обслуживание',
diff --git a/lang/sk/settings.php b/lang/sk/settings.php
index 04855a7f9..67671f6f8 100644
--- a/lang/sk/settings.php
+++ b/lang/sk/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Údržba',
diff --git a/lang/sl/settings.php b/lang/sl/settings.php
index 6eaed0a17..947621389 100644
--- a/lang/sl/settings.php
+++ b/lang/sl/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Vzdrževanje',
diff --git a/lang/sq/settings.php b/lang/sq/settings.php
index c68605fe1..c4d1eb136 100644
--- a/lang/sq/settings.php
+++ b/lang/sq/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/sr/settings.php b/lang/sr/settings.php
index d34ff3f3b..f6c86827e 100644
--- a/lang/sr/settings.php
+++ b/lang/sr/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Одржавање',
diff --git a/lang/sv/entities.php b/lang/sv/entities.php
index 08c44ff1f..680e0908a 100644
--- a/lang/sv/entities.php
+++ b/lang/sv/entities.php
@@ -397,7 +397,7 @@ return [
'comment' => 'Kommentar',
'comments' => 'Kommentarer',
'comment_add' => 'Lägg till kommentar',
- 'comment_none' => 'No comments to display',
+ 'comment_none' => 'Inga kommentarer att visa',
'comment_placeholder' => 'Lämna en kommentar här',
'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
'comment_archived_count' => ':count Archived',
diff --git a/lang/sv/preferences.php b/lang/sv/preferences.php
index 492081e59..7ebf26813 100644
--- a/lang/sv/preferences.php
+++ b/lang/sv/preferences.php
@@ -5,7 +5,7 @@
*/
return [
- 'my_account' => 'My Account',
+ 'my_account' => 'Mitt Konto',
'shortcuts' => 'Genvägar',
'shortcuts_interface' => 'UI Shortcut Preferences',
diff --git a/lang/sv/settings.php b/lang/sv/settings.php
index 2e86241da..773c4bff3 100644
--- a/lang/sv/settings.php
+++ b/lang/sv/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Underhåll',
diff --git a/lang/tk/settings.php b/lang/tk/settings.php
index c68605fe1..c4d1eb136 100644
--- a/lang/tk/settings.php
+++ b/lang/tk/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/tr/settings.php b/lang/tr/settings.php
index 71d56000f..a33d3e0ac 100644
--- a/lang/tr/settings.php
+++ b/lang/tr/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Bakım',
diff --git a/lang/uk/settings.php b/lang/uk/settings.php
index 55966c01c..afeb2c489 100644
--- a/lang/uk/settings.php
+++ b/lang/uk/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Спочатку розділи',
'sort_rule_op_chapters_last' => 'Розділи останні',
'sorting_page_limits' => 'Обмеження відображення сторінок',
- 'sorting_page_limits_desc' => 'Кількість елементів для відображення в різних списках в системі. Зазвичай менша кількість буде більш продуктивною, в той час як більша кількість уникає необхідність натискання на кілька сторінок. Рекомендується використовувати парне кратне 3 (18, 24, 30 тощо).',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Обслуговування',
diff --git a/lang/uz/settings.php b/lang/uz/settings.php
index ad191143f..259aee71a 100644
--- a/lang/uz/settings.php
+++ b/lang/uz/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapters First',
'sort_rule_op_chapters_last' => 'Chapters Last',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Xizmat',
diff --git a/lang/vi/settings.php b/lang/vi/settings.php
index 69eadddd3..f5f2377c8 100644
--- a/lang/vi/settings.php
+++ b/lang/vi/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chương trước',
'sort_rule_op_chapters_last' => 'Chương sau',
'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => 'Bảo trì',
diff --git a/lang/zh_CN/editor.php b/lang/zh_CN/editor.php
index 6dbca56e3..4bac4cf0d 100644
--- a/lang/zh_CN/editor.php
+++ b/lang/zh_CN/editor.php
@@ -48,7 +48,7 @@ return [
'superscript' => '上标',
'subscript' => '下标',
'text_color' => '文本颜色',
- 'highlight_color' => 'Highlight color',
+ 'highlight_color' => '高亮颜色',
'custom_color' => '自定义颜色',
'remove_color' => '移除颜色',
'background_color' => '背景色',
diff --git a/lang/zh_CN/entities.php b/lang/zh_CN/entities.php
index 826a8ec1e..c4ec0414d 100644
--- a/lang/zh_CN/entities.php
+++ b/lang/zh_CN/entities.php
@@ -63,10 +63,10 @@ return [
'import_delete_desc' => '这将删除上传的ZIP文件,不能撤消。',
'import_errors' => '导入错误',
'import_errors_desc' => '在尝试导入过程中出现了以下错误:',
- 'breadcrumb_siblings_for_page' => 'Navigate siblings for page',
- 'breadcrumb_siblings_for_chapter' => 'Navigate siblings for chapter',
- 'breadcrumb_siblings_for_book' => 'Navigate siblings for book',
- 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf',
+ 'breadcrumb_siblings_for_page' => '导航页面',
+ 'breadcrumb_siblings_for_chapter' => '导航章节',
+ 'breadcrumb_siblings_for_book' => '导航书籍',
+ 'breadcrumb_siblings_for_bookshelf' => '导航书架',
// Permissions and restrictions
'permissions' => '权限',
@@ -399,7 +399,7 @@ return [
'comment_add' => '添加评论',
'comment_none' => '没有要显示的评论',
'comment_placeholder' => '在这里评论',
- 'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
+ 'comment_thread_count' => ':count 条',
'comment_archived_count' => ':count 条评论已存档',
'comment_archived_threads' => '已存档的贴子',
'comment_save' => '保存评论',
diff --git a/lang/zh_CN/errors.php b/lang/zh_CN/errors.php
index 74814c6b0..98b264072 100644
--- a/lang/zh_CN/errors.php
+++ b/lang/zh_CN/errors.php
@@ -109,7 +109,7 @@ return [
'import_zip_cant_read' => '无法读取 ZIP 文件。',
'import_zip_cant_decode_data' => '无法找到并解码 ZIP data.json 内容。',
'import_zip_no_data' => 'ZIP 文件数据没有预期的书籍、章节或页面内容。',
- 'import_zip_data_too_large' => 'ZIP data.json content exceeds the configured application maximum upload size.',
+ 'import_zip_data_too_large' => '超出最大上传大小。',
'import_validation_failed' => '导入 ZIP 验证失败,出现错误:',
'import_zip_failed_notification' => 'ZIP 文件导入失败。',
'import_perms_books' => '您缺少创建书籍所需的权限。',
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => '给已给出的API所提供的密钥不正确',
'api_user_no_api_permission' => '使用过的 API 令牌的所有者没有进行API 调用的权限',
'api_user_token_expired' => '所使用的身份令牌已过期',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => '使用基于 Cookie 的身份验证 API 时,仅允许 GET 请求。',
// Settings & Maintenance
'maintenance_test_email_failure' => '发送测试电子邮件时出现错误:',
diff --git a/lang/zh_CN/notifications.php b/lang/zh_CN/notifications.php
index e4eebf5cc..55fa6824a 100644
--- a/lang/zh_CN/notifications.php
+++ b/lang/zh_CN/notifications.php
@@ -11,8 +11,8 @@ return [
'updated_page_subject' => '页面更新::pageName',
'updated_page_intro' => ':appName: 中的一个页面已被更新',
'updated_page_debounce' => '为了防止出现大量通知,一段时间内您不会收到同一编辑者再次编辑本页面的通知。',
- 'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
- 'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
+ 'comment_mention_subject' => '在页面中被提及::pageName',
+ 'comment_mention_intro' => '在 :appName 中被提及:',
'detail_page_name' => '页面名称:',
'detail_page_path' => '页面路径:',
diff --git a/lang/zh_CN/preferences.php b/lang/zh_CN/preferences.php
index f89448dd3..0a1f165bb 100644
--- a/lang/zh_CN/preferences.php
+++ b/lang/zh_CN/preferences.php
@@ -23,7 +23,7 @@ return [
'notifications_desc' => '控制在系统内发生某些活动时您会收到的电子邮件通知。',
'notifications_opt_own_page_changes' => '在我拥有的页面被修改时通知我',
'notifications_opt_own_page_comments' => '在我拥有的页面上有新评论时通知我',
- 'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
+ 'notifications_opt_comment_mentions' => '当我在评论中被提及时通知我',
'notifications_opt_comment_replies' => '在有人回复我的频率时通知我',
'notifications_save' => '保存偏好设置',
'notifications_update_success' => '通知偏好设置已更新!',
diff --git a/lang/zh_CN/settings.php b/lang/zh_CN/settings.php
index 3469752bf..e53e67aba 100644
--- a/lang/zh_CN/settings.php
+++ b/lang/zh_CN/settings.php
@@ -75,8 +75,8 @@ return [
'reg_confirm_restrict_domain_placeholder' => '尚未设置限制',
// Sorting Settings
- 'sorting' => 'Lists & Sorting',
- 'sorting_book_default' => 'Default Book Sort Rule',
+ 'sorting' => '列表和排序',
+ 'sorting_book_default' => '默认排序规则',
'sorting_book_default_desc' => '选择要应用于新书的默认排序规则。这不会影响现有书,并且可以每本书覆盖。',
'sorting_rules' => '排序规则',
'sorting_rules_desc' => '这些是预定义的排序操作,可应用于系统中的内容。',
@@ -103,8 +103,8 @@ return [
'sort_rule_op_updated_date' => '更新时间',
'sort_rule_op_chapters_first' => '章节正序',
'sort_rule_op_chapters_last' => '章节倒序',
- 'sorting_page_limits' => 'Per-Page Display Limits',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using an even multiple of 3 (18, 24, 30, etc...) is recommended.',
+ 'sorting_page_limits' => '每页显示限制',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => '维护',
@@ -197,13 +197,13 @@ return [
'role_import_content' => '导入内容',
'role_editor_change' => '更改页面编辑器',
'role_notifications' => '管理和接收通知',
- 'role_permission_note_users_and_roles' => 'These permissions will technically also provide visibility & searching of users & roles in the system.',
+ 'role_permission_note_users_and_roles' => '从技术上讲,这些权限还将提供对系统中用户和角色的可见性和搜索功能。',
'role_asset' => '资源许可',
'roles_system_warning' => '请注意,拥有以上三个权限中的任何一个都会允许用户更改自己的权限或系统中其他人的权限。 请只将拥有这些权限的角色分配给你信任的用户。',
'role_asset_desc' => '对系统内资源的默认访问许可将由这些权限控制。单独设置在书籍、章节和页面上的权限将覆盖这里的权限设定。',
'role_asset_admins' => '管理员可自动获得对所有内容的访问权限,但这些选项可能会显示或隐藏UI选项。',
'role_asset_image_view_note' => '这与图像管理器中的可见性有关。已经上传的图片的实际访问取决于系统图像存储选项。',
- 'role_asset_users_note' => 'These permissions will technically also provide visibility & searching of users in the system.',
+ 'role_asset_users_note' => '从技术上讲,这些权限还将提供对系统中用户和角色的可见性和搜索功能。',
'role_all' => '全部的',
'role_own' => '拥有的',
'role_controlled_by_asset' => '由其所在的资源来控制',
diff --git a/lang/zh_CN/validation.php b/lang/zh_CN/validation.php
index 748c8f567..955381ccb 100644
--- a/lang/zh_CN/validation.php
+++ b/lang/zh_CN/validation.php
@@ -106,7 +106,7 @@ return [
'uploaded' => '无法上传文件。 服务器可能不接受此大小的文件。',
'zip_file' => ':attribute 需要引用 ZIP 内的文件。',
- 'zip_file_size' => 'The file :attribute must not exceed :size MB.',
+ 'zip_file_size' => ':attribute 不能超过 :size MB 。',
'zip_file_mime' => ':attribute 需要引用类型为 :validTypes 的文件,找到 :foundType 。',
'zip_model_expected' => '预期的数据对象,但找到了 ":type" 。',
'zip_unique' => '对于 ZIP 中的对象类型来说,:attribute 必须是唯一的。',
diff --git a/lang/zh_TW/settings.php b/lang/zh_TW/settings.php
index 0d5d760dd..65778f77c 100644
--- a/lang/zh_TW/settings.php
+++ b/lang/zh_TW/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => '第一章',
'sort_rule_op_chapters_last' => '最後一章',
'sorting_page_limits' => '每頁顯示限制',
- 'sorting_page_limits_desc' => '設定系統內各類清單每頁顯示的項目數量。通常較低的數量能提升效能表現,而較高的數量則可避免使用者需點擊翻閱多頁。建議採用 3 的整數倍數(如 18、24、30 等)。',
+ 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
// Maintenance settings
'maint' => '維護',
From 362859ac23761631949089de61df338ed002ca97 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 15 Mar 2026 13:14:54 +0000
Subject: [PATCH 09/85] Updated translator & dependency attribution before
release v26.03
---
.github/translators.txt | 3 +++
dev/licensing/js-library-licenses.txt | 19 -------------------
dev/licensing/php-library-licenses.txt | 2 +-
3 files changed, 4 insertions(+), 20 deletions(-)
diff --git a/.github/translators.txt b/.github/translators.txt
index 14c51bd0b..fcbb9675d 100644
--- a/.github/translators.txt
+++ b/.github/translators.txt
@@ -530,3 +530,6 @@ Shadluk Avan (quldosh) :: Uzbek
Marci (MartonPoto) :: Hungarian
Michał Sadurski (wheeskeey) :: Polish
JanDziaslo :: Polish
+Charllys Fernandes (CharllysFernandes) :: Portuguese, Brazilian
+Ilgiz Zigangirov (inov8) :: Russian
+Max Israelsson (Blezie) :: Swedish
diff --git a/dev/licensing/js-library-licenses.txt b/dev/licensing/js-library-licenses.txt
index d7ad4ecc8..1c23c0937 100644
--- a/dev/licensing/js-library-licenses.txt
+++ b/dev/licensing/js-library-licenses.txt
@@ -3321,20 +3321,6 @@ Copyright: Copyright 2022 Romain Menke, Antonio Laguna <*******@******.**>
Source: git+https://github.com/csstools/postcss-plugins.git
Link: https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer#readme
-----------
-@emnapi/core
-License: MIT
-License File: node_modules/@emnapi/core/LICENSE
-Copyright: Copyright (c) 2021-present Toyobayashi
-Source: git+https://github.com/toyobayashi/emnapi.git
-Link: https://github.com/toyobayashi/emnapi#readme
------------
-@emnapi/runtime
-License: MIT
-License File: node_modules/@emnapi/runtime/LICENSE
-Copyright: Copyright (c) 2021-present Toyobayashi
-Source: git+https://github.com/toyobayashi/emnapi.git
-Link: https://github.com/toyobayashi/emnapi#readme
------------
@esbuild/linux-x64
License: MIT
Source: git+https://github.com/evanw/esbuild.git
@@ -3784,11 +3770,6 @@ Copyright: Copyright (c) Microsoft Corporation.
Source: https://github.com/tsconfig/bases.git
Link: https://github.com/tsconfig/bases.git
-----------
-@tybys/wasm-util
-License: MIT
-Source: https://github.com/toyobayashi/wasm-util.git
-Link: https://github.com/toyobayashi/wasm-util.git
------------
@types/babel__core
License: MIT
License File: node_modules/@types/babel__core/LICENSE
diff --git a/dev/licensing/php-library-licenses.txt b/dev/licensing/php-library-licenses.txt
index 6ae0b86b9..348fce2ff 100644
--- a/dev/licensing/php-library-licenses.txt
+++ b/dev/licensing/php-library-licenses.txt
@@ -8,7 +8,7 @@ aws/aws-sdk-php
License: Apache-2.0
License File: vendor/aws/aws-sdk-php/LICENSE
Source: https://github.com/aws/aws-sdk-php.git
-Link: http://aws.amazon.com/sdkforphp
+Link: https://aws.amazon.com/sdk-for-php
-----------
bacon/bacon-qr-code
License: BSD-2-Clause
From 4f18fea08691bc0cfb2beab0e4495c16f1c8bdce Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 15 Mar 2026 13:17:48 +0000
Subject: [PATCH 10/85] Deps: Updated PHP deps pre v26.03 release
---
composer.lock | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/composer.lock b/composer.lock
index d8ea00662..030cd1a73 100644
--- a/composer.lock
+++ b/composer.lock
@@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.373.0",
+ "version": "3.373.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "fb74a2dca7ae2363e929c5cea33a4a4db0d22690"
+ "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/fb74a2dca7ae2363e929c5cea33a4a4db0d22690",
- "reference": "fb74a2dca7ae2363e929c5cea33a4a4db0d22690",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/483fba51c28b3a0c0647bf5100e0edca82090b18",
+ "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18",
"shasum": ""
},
"require": {
@@ -92,12 +92,12 @@
"aws/aws-php-sns-message-validator": "~1.0",
"behat/behat": "~3.0",
"composer/composer": "^2.7.8",
- "dms/phpunit-arraysubset-asserts": "^0.4.0",
+ "dms/phpunit-arraysubset-asserts": "^v0.5.0",
"doctrine/cache": "~1.4",
"ext-dom": "*",
"ext-openssl": "*",
"ext-sockets": "*",
- "phpunit/phpunit": "^9.6",
+ "phpunit/phpunit": "^10.0",
"psr/cache": "^2.0 || ^3.0",
"psr/simple-cache": "^2.0 || ^3.0",
"sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
@@ -153,9 +153,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.373.0"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.373.2"
},
- "time": "2026-03-11T18:33:36+00:00"
+ "time": "2026-03-13T18:08:30+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -4941,16 +4941,16 @@
},
{
"name": "robrichards/xmlseclibs",
- "version": "3.1.4",
+ "version": "3.1.5",
"source": {
"type": "git",
"url": "https://github.com/robrichards/xmlseclibs.git",
- "reference": "bc87389224c6de95802b505e5265b0ec2c5bcdbd"
+ "reference": "03062be78178cbb5e8f605cd255dc32a14981f92"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/bc87389224c6de95802b505e5265b0ec2c5bcdbd",
- "reference": "bc87389224c6de95802b505e5265b0ec2c5bcdbd",
+ "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/03062be78178cbb5e8f605cd255dc32a14981f92",
+ "reference": "03062be78178cbb5e8f605cd255dc32a14981f92",
"shasum": ""
},
"require": {
@@ -4977,9 +4977,9 @@
],
"support": {
"issues": "https://github.com/robrichards/xmlseclibs/issues",
- "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.4"
+ "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.5"
},
- "time": "2025-12-08T11:57:53+00:00"
+ "time": "2026-03-13T10:31:56+00:00"
},
{
"name": "sabberworm/php-css-parser",
From a9ffd3e0c77019916bba21830d8514cca723ce17 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Mon, 16 Mar 2026 18:28:44 +0000
Subject: [PATCH 11/85] Responses: Added extra sanitization for download names
From testing, don't think this could exploited directly, as the response
would error instead of allowing control characters, but this adds an
extra layer of sanitization, and switches to encoded disposition
filenames for better UTF8 support.
---
app/Http/DownloadResponseFactory.php | 7 +++++--
tests/Api/ExportsApiTest.php | 30 ++++++++++++++--------------
tests/Exports/HtmlExportTest.php | 6 +++---
tests/Exports/MarkdownExportTest.php | 2 +-
tests/Exports/PdfExportTest.php | 6 +++---
tests/Exports/TextExportTest.php | 6 +++---
tests/Uploads/AttachmentTest.php | 19 ++++++++++++++++--
7 files changed, 47 insertions(+), 29 deletions(-)
diff --git a/app/Http/DownloadResponseFactory.php b/app/Http/DownloadResponseFactory.php
index 8384484ad..1b6256c00 100644
--- a/app/Http/DownloadResponseFactory.php
+++ b/app/Http/DownloadResponseFactory.php
@@ -102,12 +102,15 @@ class DownloadResponseFactory
protected function getHeaders(string $fileName, int $fileSize, string $mime = 'application/octet-stream'): array
{
$disposition = ($mime === 'application/octet-stream') ? 'attachment' : 'inline';
- $downloadName = str_replace('"', '', $fileName);
+
+ $downloadName = str_replace(['"', '/', '\\', '$'], '', $fileName);
+ $downloadName = preg_replace('/[\x00-\x1F\x7F]/', '', $downloadName);
+ $encodedDownloadName = rawurlencode($downloadName);
return [
'Content-Type' => $mime,
'Content-Length' => $fileSize,
- 'Content-Disposition' => "{$disposition}; filename=\"{$downloadName}\"",
+ 'Content-Disposition' => "{$disposition}; filename*=UTF-8''{$encodedDownloadName}",
'X-Content-Type-Options' => 'nosniff',
];
}
diff --git a/tests/Api/ExportsApiTest.php b/tests/Api/ExportsApiTest.php
index e1ac698d0..7951e04d2 100644
--- a/tests/Api/ExportsApiTest.php
+++ b/tests/Api/ExportsApiTest.php
@@ -19,7 +19,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/books/{$book->id}/export/html");
$resp->assertStatus(200);
$resp->assertSee($book->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.html');
}
public function test_book_plain_text_endpoint()
@@ -30,7 +30,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/books/{$book->id}/export/plaintext");
$resp->assertStatus(200);
$resp->assertSee($book->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.txt');
}
public function test_book_pdf_endpoint()
@@ -40,7 +40,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/books/{$book->id}/export/pdf");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.pdf');
}
public function test_book_markdown_endpoint()
@@ -50,7 +50,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/books/{$book->id}/export/markdown");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.md"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.md');
$resp->assertSee('# ' . $book->name);
$resp->assertSee('# ' . $book->pages()->first()->name);
$resp->assertSee('# ' . $book->chapters()->first()->name);
@@ -63,7 +63,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/books/{$book->id}/export/zip");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.zip"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.zip');
$zip = ZipTestHelper::extractFromZipResponse($resp);
$this->assertArrayHasKey('book', $zip->data);
@@ -77,7 +77,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/chapters/{$chapter->id}/export/html");
$resp->assertStatus(200);
$resp->assertSee($chapter->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.html');
}
public function test_chapter_plain_text_endpoint()
@@ -88,7 +88,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/chapters/{$chapter->id}/export/plaintext");
$resp->assertStatus(200);
$resp->assertSee($chapter->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.txt');
}
public function test_chapter_pdf_endpoint()
@@ -98,7 +98,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/chapters/{$chapter->id}/export/pdf");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.pdf');
}
public function test_chapter_markdown_endpoint()
@@ -108,7 +108,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/chapters/{$chapter->id}/export/markdown");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.md');
$resp->assertSee('# ' . $chapter->name);
$resp->assertSee('# ' . $chapter->pages()->first()->name);
}
@@ -120,7 +120,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/chapters/{$chapter->id}/export/zip");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.zip"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.zip');
$zip = ZipTestHelper::extractFromZipResponse($resp);
$this->assertArrayHasKey('chapter', $zip->data);
@@ -134,7 +134,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/pages/{$page->id}/export/html");
$resp->assertStatus(200);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.html');
}
public function test_page_plain_text_endpoint()
@@ -145,7 +145,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/pages/{$page->id}/export/plaintext");
$resp->assertStatus(200);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.txt');
}
public function test_page_pdf_endpoint()
@@ -155,7 +155,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/pages/{$page->id}/export/pdf");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.pdf');
}
public function test_page_markdown_endpoint()
@@ -166,7 +166,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/pages/{$page->id}/export/markdown");
$resp->assertStatus(200);
$resp->assertSee('# ' . $page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.md');
}
public function test_page_zip_endpoint()
@@ -176,7 +176,7 @@ class ExportsApiTest extends TestCase
$resp = $this->get("/api/pages/{$page->id}/export/zip");
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.zip"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.zip');
$zip = ZipTestHelper::extractFromZipResponse($resp);
$this->assertArrayHasKey('page', $zip->data);
diff --git a/tests/Exports/HtmlExportTest.php b/tests/Exports/HtmlExportTest.php
index e039fb2cc..f23352e0e 100644
--- a/tests/Exports/HtmlExportTest.php
+++ b/tests/Exports/HtmlExportTest.php
@@ -18,7 +18,7 @@ class HtmlExportTest extends TestCase
$resp = $this->get($page->getUrl('/export/html'));
$resp->assertStatus(200);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.html');
}
public function test_book_html_export()
@@ -31,7 +31,7 @@ class HtmlExportTest extends TestCase
$resp->assertStatus(200);
$resp->assertSee($book->name);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.html');
}
public function test_book_html_export_shows_html_descriptions()
@@ -58,7 +58,7 @@ class HtmlExportTest extends TestCase
$resp->assertStatus(200);
$resp->assertSee($chapter->name);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.html');
}
public function test_chapter_html_export_shows_html_descriptions()
diff --git a/tests/Exports/MarkdownExportTest.php b/tests/Exports/MarkdownExportTest.php
index 09928ced2..ac1e283f3 100644
--- a/tests/Exports/MarkdownExportTest.php
+++ b/tests/Exports/MarkdownExportTest.php
@@ -14,7 +14,7 @@ class MarkdownExportTest extends TestCase
$resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
$resp->assertStatus(200);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.md');
}
public function test_page_markdown_export_uses_existing_markdown_if_apparent()
diff --git a/tests/Exports/PdfExportTest.php b/tests/Exports/PdfExportTest.php
index e4de87d0d..f311f8457 100644
--- a/tests/Exports/PdfExportTest.php
+++ b/tests/Exports/PdfExportTest.php
@@ -17,7 +17,7 @@ class PdfExportTest extends TestCase
$resp = $this->get($page->getUrl('/export/pdf'));
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.pdf');
}
public function test_book_pdf_export()
@@ -28,7 +28,7 @@ class PdfExportTest extends TestCase
$resp = $this->get($book->getUrl('/export/pdf'));
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.pdf');
}
public function test_chapter_pdf_export()
@@ -38,7 +38,7 @@ class PdfExportTest extends TestCase
$resp = $this->get($chapter->getUrl('/export/pdf'));
$resp->assertStatus(200);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.pdf');
}
diff --git a/tests/Exports/TextExportTest.php b/tests/Exports/TextExportTest.php
index c593a6585..4b2d62887 100644
--- a/tests/Exports/TextExportTest.php
+++ b/tests/Exports/TextExportTest.php
@@ -14,7 +14,7 @@ class TextExportTest extends TestCase
$resp = $this->get($page->getUrl('/export/plaintext'));
$resp->assertStatus(200);
$resp->assertSee($page->name);
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $page->slug . '.txt');
}
public function test_book_text_export()
@@ -35,7 +35,7 @@ class TextExportTest extends TestCase
$resp->assertSee($directPage->name);
$resp->assertSee('My awesome page');
$resp->assertSee('My little nested page');
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $book->slug . '.txt');
}
public function test_book_text_export_format()
@@ -68,7 +68,7 @@ class TextExportTest extends TestCase
$resp->assertSee($chapter->name);
$resp->assertSee($page->name);
$resp->assertSee('This is content within the page!');
- $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"');
+ $resp->assertHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . $chapter->slug . '.txt');
}
public function test_chapter_text_export_format()
diff --git a/tests/Uploads/AttachmentTest.php b/tests/Uploads/AttachmentTest.php
index b443ca9f1..945fc258d 100644
--- a/tests/Uploads/AttachmentTest.php
+++ b/tests/Uploads/AttachmentTest.php
@@ -324,7 +324,7 @@ class AttachmentTest extends TestCase
$attachmentGet = $this->get($attachment->getUrl(true));
// http-foundation/Response does some 'fixing' of responses to add charsets to text responses.
$attachmentGet->assertHeader('Content-Type', 'text/plain; charset=utf-8');
- $attachmentGet->assertHeader('Content-Disposition', 'inline; filename="upload_test_file.txt"');
+ $attachmentGet->assertHeader('Content-Disposition', 'inline; filename*=UTF-8\'\'upload_test_file.txt');
$attachmentGet->assertHeader('X-Content-Type-Options', 'nosniff');
$this->files->deleteAllAttachmentFiles();
@@ -340,7 +340,22 @@ class AttachmentTest extends TestCase
$attachmentGet = $this->get($attachment->getUrl(true));
// http-foundation/Response does some 'fixing' of responses to add charsets to text responses.
$attachmentGet->assertHeader('Content-Type', 'text/plain; charset=utf-8');
- $attachmentGet->assertHeader('Content-Disposition', 'inline; filename="test_file.html"');
+ $attachmentGet->assertHeader('Content-Disposition', 'inline; filename*=UTF-8\'\'test_file.html');
+
+ $this->files->deleteAllAttachmentFiles();
+ }
+
+ public function test_file_access_name_in_content_disposition_header_is_sanitized()
+ {
+ $page = $this->entities->page();
+ $this->asAdmin();
+
+ $attachment = $this->files->uploadAttachmentDataToPage($this, $page, 'test_file.html', '
testing
', 'text/html');
+ $attachment->name = "my\\_/super\n_fu\$n_\tfile";
+ $attachment->save();
+
+ $attachmentGet = $this->get($attachment->getUrl(true));
+ $attachmentGet->assertHeader('Content-Disposition', 'inline; filename*=UTF-8\'\'my_super_fun_file.html');
$this->files->deleteAllAttachmentFiles();
}
From 0120b475eb18c7d4e1851a937cafd12724fabb31 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Tue, 17 Mar 2026 10:59:11 +0000
Subject: [PATCH 12/85] Deps: Updated PHP deps pre v26.03.1
---
composer.lock | 58 +++++++++++++++++++++++++--------------------------
1 file changed, 29 insertions(+), 29 deletions(-)
diff --git a/composer.lock b/composer.lock
index 030cd1a73..e1e56ff9d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.373.2",
+ "version": "3.373.3",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18"
+ "reference": "d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/483fba51c28b3a0c0647bf5100e0edca82090b18",
- "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5",
+ "reference": "d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5",
"shasum": ""
},
"require": {
@@ -153,22 +153,22 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.373.2"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.373.3"
},
- "time": "2026-03-13T18:08:30+00:00"
+ "time": "2026-03-16T18:15:27+00:00"
},
{
"name": "bacon/bacon-qr-code",
- "version": "v3.0.3",
+ "version": "v3.0.4",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
- "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563"
+ "reference": "3feed0e212b8412cc5d2612706744789b0615824"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563",
- "reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563",
+ "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/3feed0e212b8412cc5d2612706744789b0615824",
+ "reference": "3feed0e212b8412cc5d2612706744789b0615824",
"shasum": ""
},
"require": {
@@ -208,9 +208,9 @@
"homepage": "https://github.com/Bacon/BaconQrCode",
"support": {
"issues": "https://github.com/Bacon/BaconQrCode/issues",
- "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3"
+ "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.4"
},
- "time": "2025-11-19T17:15:36+00:00"
+ "time": "2026-03-16T01:01:30+00:00"
},
{
"name": "brick/math",
@@ -2943,20 +2943,20 @@
},
{
"name": "league/uri",
- "version": "7.8.0",
+ "version": "7.8.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/uri.git",
- "reference": "4436c6ec8d458e4244448b069cc572d088230b76"
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76",
- "reference": "4436c6ec8d458e4244448b069cc572d088230b76",
+ "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4",
+ "reference": "08cf38e3924d4f56238125547b5720496fac8fd4",
"shasum": ""
},
"require": {
- "league/uri-interfaces": "^7.8",
+ "league/uri-interfaces": "^7.8.1",
"php": "^8.1",
"psr/http-factory": "^1"
},
@@ -3029,7 +3029,7 @@
"docs": "https://uri.thephpleague.com",
"forum": "https://thephpleague.slack.com",
"issues": "https://github.com/thephpleague/uri-src/issues",
- "source": "https://github.com/thephpleague/uri/tree/7.8.0"
+ "source": "https://github.com/thephpleague/uri/tree/7.8.1"
},
"funding": [
{
@@ -3037,20 +3037,20 @@
"type": "github"
}
],
- "time": "2026-01-14T17:24:56+00:00"
+ "time": "2026-03-15T20:22:25+00:00"
},
{
"name": "league/uri-interfaces",
- "version": "7.8.0",
+ "version": "7.8.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/uri-interfaces.git",
- "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4"
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4",
- "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4",
+ "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928",
+ "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928",
"shasum": ""
},
"require": {
@@ -3113,7 +3113,7 @@
"docs": "https://uri.thephpleague.com",
"forum": "https://thephpleague.slack.com",
"issues": "https://github.com/thephpleague/uri-src/issues",
- "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0"
+ "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1"
},
"funding": [
{
@@ -3121,7 +3121,7 @@
"type": "github"
}
],
- "time": "2026-01-15T06:54:53+00:00"
+ "time": "2026-03-08T20:05:35+00:00"
},
{
"name": "masterminds/html5",
@@ -9169,11 +9169,11 @@
},
{
"name": "phpstan/phpstan",
- "version": "2.1.40",
+ "version": "2.1.41",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
- "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300",
+ "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300",
"shasum": ""
},
"require": {
@@ -9218,7 +9218,7 @@
"type": "github"
}
],
- "time": "2026-02-23T15:04:35+00:00"
+ "time": "2026-03-16T18:24:10+00:00"
},
{
"name": "phpunit/php-code-coverage",
From 04dd9f8e19301fde58218864ae51eef6b678f27a Mon Sep 17 00:00:00 2001
From: Rodrigo Primo
Date: Tue, 17 Mar 2026 17:21:01 -0300
Subject: [PATCH 13/85] Update PHP_CodeSniffer repository link
---
dev/docs/development.md | 2 +-
readme.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/dev/docs/development.md b/dev/docs/development.md
index 418e9ead6..2c73a0256 100644
--- a/dev/docs/development.md
+++ b/dev/docs/development.md
@@ -37,7 +37,7 @@ We use tools to manage code standards and formatting within the project. If subm
### PHP
-PHP code standards are managed by [using PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer).
+PHP code standards are managed by [using PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer).
Static analysis is in place using [PHPStan](https://phpstan.org/) & [Larastan](https://github.com/nunomaduro/larastan).
The below commands can be used to utilise these tools:
diff --git a/readme.md b/readme.md
index 194f81e4a..00eb13504 100644
--- a/readme.md
+++ b/readme.md
@@ -178,7 +178,7 @@ Note: This is not an exhaustive list of all libraries and projects that would be
* [phpseclib](https://github.com/phpseclib/phpseclib) - _[MIT](https://github.com/phpseclib/phpseclib/blob/master/LICENSE)_
* [Clockwork](https://github.com/itsgoingd/clockwork) - _[MIT](https://github.com/itsgoingd/clockwork/blob/master/LICENSE)_
* [PHPStan](https://phpstan.org/) & [Larastan](https://github.com/nunomaduro/larastan) - _[MIT](https://github.com/phpstan/phpstan/blob/master/LICENSE) and [MIT](https://github.com/nunomaduro/larastan/blob/master/LICENSE.md)_
-* [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) - _[BSD 3-Clause](https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt)_
+* [PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) - _[BSD 3-Clause](https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt)_
* [JakeArchibald/IDB-Keyval](https://github.com/jakearchibald/idb-keyval) - _[Apache-2.0](https://github.com/jakearchibald/idb-keyval/blob/main/LICENCE)_
* [HTML Purifier](https://github.com/ezyang/htmlpurifier) and [htmlpurifier-html5](https://github.com/xemlock/htmlpurifier-html5) - _[LGPL-2.1](https://github.com/ezyang/htmlpurifier/blob/master/LICENSE) and [MIT](https://github.com/xemlock/htmlpurifier-html5/blob/master/LICENSE)_
From 5763d26b175769675f78ce5955601313ac772a15 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Thu, 19 Mar 2026 21:29:30 +0000
Subject: [PATCH 14/85] Updated registration to use validated input instead of
all
---
app/Access/Controllers/RegisterController.php | 3 +--
app/Access/RegistrationService.php | 6 ++---
tests/Auth/RegistrationTest.php | 24 +++++++++++++++++++
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/app/Access/Controllers/RegisterController.php b/app/Access/Controllers/RegisterController.php
index e9812aa5d..f0261fba8 100644
--- a/app/Access/Controllers/RegisterController.php
+++ b/app/Access/Controllers/RegisterController.php
@@ -48,8 +48,7 @@ class RegisterController extends Controller
public function postRegister(Request $request)
{
$this->registrationService->ensureRegistrationAllowed();
- $this->validator($request->all())->validate();
- $userData = $request->all();
+ $userData = $this->validator($request->all())->validate();
try {
$user = $this->registrationService->registerUser($userData);
diff --git a/app/Access/RegistrationService.php b/app/Access/RegistrationService.php
index 68992fbc6..e47479e79 100644
--- a/app/Access/RegistrationService.php
+++ b/app/Access/RegistrationService.php
@@ -83,7 +83,7 @@ class RegistrationService
// Email restriction
$this->ensureEmailDomainAllowed($userEmail);
- // Ensure user does not already exist
+ // Ensure the user does not already exist
$alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
if ($alreadyUser) {
throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
@@ -99,7 +99,7 @@ class RegistrationService
$newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed);
$newUser->attachDefaultRole();
- // Assign social account if given
+ // Assign a social account if given
if ($socialAccount) {
$newUser->socialAccounts()->save($socialAccount);
}
@@ -107,7 +107,7 @@ class RegistrationService
Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser);
- // Start email confirmation flow if required
+ // Start the email confirmation flow if required
if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
$newUser->save();
diff --git a/tests/Auth/RegistrationTest.php b/tests/Auth/RegistrationTest.php
index 2666fa3b4..e0d7c2626 100644
--- a/tests/Auth/RegistrationTest.php
+++ b/tests/Auth/RegistrationTest.php
@@ -188,6 +188,30 @@ class RegistrationTest extends TestCase
$resp->assertSee('The password must be at least 8 characters.');
}
+ public function test_registration_input_filtered_to_validated_input()
+ {
+ $this->setSettings(['registration-enabled' => 'true']);
+ $roleIds = Role::all()->pluck('id')->toArray();
+
+ $resp = $this->post('/register', [
+ 'name' => 'Barry',
+ 'email' => 'barry@example.com',
+ 'password' => 'superpassword',
+ 'password_confirmation' => 'superpassword',
+ 'external_auth_id' => 'ext5691284',
+ 'roles' => $roleIds,
+ ]);
+
+ $resp->assertRedirect('/');
+
+ /** @var User $user */
+ $user = auth()->user();
+ $this->assertNotNull($user);
+ $this->assertFalse($user->isGuest());
+ $this->assertEmpty($user->external_auth_id);
+ $this->assertEquals(0, $user->roles()->count());
+ }
+
public function test_registration_simple_honeypot_active()
{
$this->setSettings(['registration-enabled' => 'true']);
From a44756168dcbce0813aaefa073f9dc7e59420636 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 22 Mar 2026 17:18:04 +0000
Subject: [PATCH 15/85] WYSIWYG: Aligned double click to set label for details
functionality
Aligned the behaviour across the WYSIWYG editors, and also for nested
details blocks (which wasn't working in the TinyMCE implementation).
Closes #6059
---
.../js/wysiwyg-tinymce/plugins-details.js | 10 ++++++++--
.../js/wysiwyg/lexical/core/LexicalEditor.ts | 19 +++++++++++++++++++
.../lexical/rich-text/LexicalDetailsNode.ts | 11 +++++++++++
.../js/wysiwyg/ui/defaults/buttons/objects.ts | 2 +-
resources/js/wysiwyg/ui/framework/manager.ts | 7 +++++--
5 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/resources/js/wysiwyg-tinymce/plugins-details.js b/resources/js/wysiwyg-tinymce/plugins-details.js
index c4a6d927d..58b0ac9d8 100644
--- a/resources/js/wysiwyg-tinymce/plugins-details.js
+++ b/resources/js/wysiwyg-tinymce/plugins-details.js
@@ -19,6 +19,8 @@ function setSummary(editor, summaryContent) {
}
summary.textContent = summaryContent;
});
+
+ editor.selection.select(details);
}
/**
@@ -202,8 +204,12 @@ function register(editor) {
});
editor.on('dblclick', event => {
- if (!getSelectedDetailsBlock(editor) || event.target.closest('doc-root')) return;
- showDetailLabelEditWindow(editor);
+ const domElClass = event?.target?.ownerDocument?.defaultView?.HTMLDetailsElement;
+ if (domElClass && event.target instanceof domElClass && getSelectedDetailsBlock(editor)) {
+ showDetailLabelEditWindow(editor);
+ event.preventDefault();
+ event.stopPropagation();
+ }
});
editor.ui.registry.addButton('toggledetails', {
diff --git a/resources/js/wysiwyg/lexical/core/LexicalEditor.ts b/resources/js/wysiwyg/lexical/core/LexicalEditor.ts
index 364f6c6b7..46660c9b7 100644
--- a/resources/js/wysiwyg/lexical/core/LexicalEditor.ts
+++ b/resources/js/wysiwyg/lexical/core/LexicalEditor.ts
@@ -45,6 +45,7 @@ import {LineBreakNode} from './nodes/LexicalLineBreakNode';
import {ParagraphNode} from './nodes/LexicalParagraphNode';
import {RootNode} from './nodes/LexicalRootNode';
import {TabNode} from './nodes/LexicalTabNode';
+import {EditorUiContext} from "../../ui/framework/core";
export type Spread = Omit & T1;
@@ -621,6 +622,8 @@ export class LexicalEditor {
_editable: boolean;
/** @internal */
_blockCursorElement: null | HTMLDivElement;
+ /** @internal */
+ _context: null | EditorUiContext;
/** @internal */
constructor(
@@ -682,6 +685,7 @@ export class LexicalEditor {
this._headless = parentEditor !== null && parentEditor._headless;
this._window = null;
this._blockCursorElement = null;
+ this._context = null;
}
/**
@@ -1285,6 +1289,21 @@ export class LexicalEditor {
triggerListeners('editable', this, true, editable);
}
}
+
+ /**
+ * Set the UI context that this editor is intended to be part of.
+ */
+ setUiContext(context: EditorUiContext) {
+ this._context = context;
+ }
+
+ /**
+ * Get the UI context that this editor is considered to be part of.
+ */
+ getUiContext(): EditorUiContext|null {
+ return this._context;
+ }
+
/**
* Returns a JSON-serializable javascript object NOT a JSON string.
* You still must call JSON.stringify (or something else) to turn the
diff --git a/resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts b/resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts
index cdf32fdcb..70ae7f0f9 100644
--- a/resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts
+++ b/resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts
@@ -9,6 +9,7 @@ import {
} from 'lexical';
import {extractDirectionFromElement} from "lexical/nodes/common";
+import {$showDetailsForm} from "../../ui/defaults/forms/objects";
export type SerializedDetailsNode = Spread<{
id: string;
@@ -90,6 +91,16 @@ export class DetailsNode extends ElementNode {
});
});
+ summary.addEventListener('dblclick', event => {
+ event.preventDefault();
+ const uiContext = _editor.getUiContext();
+ if (uiContext) {
+ _editor.read(() => {
+ $showDetailsForm(this, uiContext);
+ });
+ }
+ });
+
el.append(summary);
return el;
diff --git a/resources/js/wysiwyg/ui/defaults/buttons/objects.ts b/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
index 00dc9500e..bd6e41da9 100644
--- a/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
+++ b/resources/js/wysiwyg/ui/defaults/buttons/objects.ts
@@ -221,7 +221,7 @@ export const detailsEditLabel: EditorButtonDefinition = {
if ($isDetailsNode(details)) {
$showDetailsForm(details, context);
}
- })
+ });
},
isActive(selection: BaseSelection | null): boolean {
return false;
diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts
index 78d0cc9a2..3b4d5b495 100644
--- a/resources/js/wysiwyg/ui/framework/manager.ts
+++ b/resources/js/wysiwyg/ui/framework/manager.ts
@@ -29,7 +29,7 @@ export class EditorUIManager {
setContext(context: EditorUiContext) {
this.context = context;
this.setupEventListeners();
- this.setupEditor(context.editor);
+ this.setupEditor(context.editor, context);
}
getContext(): EditorUiContext {
@@ -256,7 +256,10 @@ export class EditorUIManager {
}
}
- protected setupEditor(editor: LexicalEditor) {
+ protected setupEditor(editor: LexicalEditor, context: EditorUiContext) {
+ // Pass the context to the editor
+ editor.setUiContext(context);
+
// Register our DOM decorate listener with the editor
const domDecorateListener: DecoratorListener = (decorators: Record) => {
editor.getEditorState().read(() => {
From 5ebc1fe3b0a071b93148f16703c6d96c4af25ccf Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 22 Mar 2026 17:22:13 +0000
Subject: [PATCH 16/85] Deps: Updated PHP package versions pre v26.03.2 release
---
composer.lock | 80 +++++++++++++++++++++++++--------------------------
1 file changed, 40 insertions(+), 40 deletions(-)
diff --git a/composer.lock b/composer.lock
index e1e56ff9d..d6069720d 100644
--- a/composer.lock
+++ b/composer.lock
@@ -62,16 +62,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.373.3",
+ "version": "3.373.7",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5"
+ "reference": "4402bd10f913e66b7271f44466be8d5ba6c9146e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5",
- "reference": "d23edc4cf9cd81cb98b5beb9c1fb3737f535b1e5",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/4402bd10f913e66b7271f44466be8d5ba6c9146e",
+ "reference": "4402bd10f913e66b7271f44466be8d5ba6c9146e",
"shasum": ""
},
"require": {
@@ -153,9 +153,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.373.3"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.373.7"
},
- "time": "2026-03-16T18:15:27+00:00"
+ "time": "2026-03-20T18:14:19+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -1801,16 +1801,16 @@
},
{
"name": "laravel/framework",
- "version": "v12.54.1",
+ "version": "v12.55.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "325497463e7599cd14224c422c6e5dd2fe832868"
+ "reference": "6d9185a248d101b07eecaf8fd60b18129545fd33"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/325497463e7599cd14224c422c6e5dd2fe832868",
- "reference": "325497463e7599cd14224c422c6e5dd2fe832868",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/6d9185a248d101b07eecaf8fd60b18129545fd33",
+ "reference": "6d9185a248d101b07eecaf8fd60b18129545fd33",
"shasum": ""
},
"require": {
@@ -1926,7 +1926,7 @@
"orchestra/testbench-core": "^10.9.0",
"pda/pheanstalk": "^5.0.6|^7.0.0",
"php-http/discovery": "^1.15",
- "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan": "^2.1.41",
"phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
"predis/predis": "^2.3|^3.0",
"resend/resend-php": "^0.10.0|^1.0",
@@ -2019,20 +2019,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-03-10T20:25:56+00:00"
+ "time": "2026-03-18T14:28:59+00:00"
},
{
"name": "laravel/prompts",
- "version": "v0.3.14",
+ "version": "v0.3.15",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
- "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176"
+ "reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/prompts/zipball/9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
- "reference": "9f0e371244eedfe2ebeaa72c79c54bb5df6e0176",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
+ "reference": "4bb8107ec97651fd3f17f897d6489dbc4d8fb999",
"shasum": ""
},
"require": {
@@ -2076,9 +2076,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
- "source": "https://github.com/laravel/prompts/tree/v0.3.14"
+ "source": "https://github.com/laravel/prompts/tree/v0.3.15"
},
- "time": "2026-03-01T09:02:38+00:00"
+ "time": "2026-03-17T13:45:17+00:00"
},
{
"name": "laravel/serializable-closure",
@@ -2281,16 +2281,16 @@
},
{
"name": "league/commonmark",
- "version": "2.8.1",
+ "version": "2.8.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "84b1ca48347efdbe775426f108622a42735a6579"
+ "reference": "59fb075d2101740c337c7216e3f32b36c204218b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579",
- "reference": "84b1ca48347efdbe775426f108622a42735a6579",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b",
+ "reference": "59fb075d2101740c337c7216e3f32b36c204218b",
"shasum": ""
},
"require": {
@@ -2384,7 +2384,7 @@
"type": "tidelift"
}
],
- "time": "2026-03-05T21:37:03+00:00"
+ "time": "2026-03-19T13:16:38+00:00"
},
{
"name": "league/config",
@@ -4027,16 +4027,16 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "3.0.49",
+ "version": "3.0.50",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "6233a1e12584754e6b5daa69fe1289b47775c1b9"
+ "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/6233a1e12584754e6b5daa69fe1289b47775c1b9",
- "reference": "6233a1e12584754e6b5daa69fe1289b47775c1b9",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
+ "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
"shasum": ""
},
"require": {
@@ -4117,7 +4117,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/3.0.49"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.50"
},
"funding": [
{
@@ -4133,7 +4133,7 @@
"type": "tidelift"
}
],
- "time": "2026-01-27T09:17:28+00:00"
+ "time": "2026-03-19T02:57:58+00:00"
},
{
"name": "pragmarx/google2fa",
@@ -5154,22 +5154,22 @@
},
{
"name": "socialiteproviders/manager",
- "version": "v4.8.1",
+ "version": "4.9.2",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
- "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4"
+ "reference": "35372dc62787e61e91cfec73f45fd5d5ae0f8891"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4",
- "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4",
+ "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/35372dc62787e61e91cfec73f45fd5d5ae0f8891",
+ "reference": "35372dc62787e61e91cfec73f45fd5d5ae0f8891",
"shasum": ""
},
"require": {
- "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
+ "illuminate/support": "^11.0 || ^12.0 || ^13.0",
"laravel/socialite": "^5.5",
- "php": "^8.1"
+ "php": "^8.2"
},
"require-dev": {
"mockery/mockery": "^1.2",
@@ -5224,7 +5224,7 @@
"issues": "https://github.com/socialiteproviders/manager/issues",
"source": "https://github.com/socialiteproviders/manager"
},
- "time": "2025-02-24T19:33:30+00:00"
+ "time": "2026-03-18T22:13:24+00:00"
},
{
"name": "socialiteproviders/microsoft-azure",
@@ -9169,11 +9169,11 @@
},
{
"name": "phpstan/phpstan",
- "version": "2.1.41",
+ "version": "2.1.42",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300",
- "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0",
+ "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0",
"shasum": ""
},
"require": {
@@ -9218,7 +9218,7 @@
"type": "github"
}
],
- "time": "2026-03-16T18:24:10+00:00"
+ "time": "2026-03-17T14:58:32+00:00"
},
{
"name": "phpunit/php-code-coverage",
From fd6867e577f271289ac6ba1490af2aef784996e8 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Mon, 23 Mar 2026 10:05:51 +0000
Subject: [PATCH 17/85] Updated translations with latest Crowdin changes
(#6064)
---
lang/cs/settings.php | 2 +-
lang/de/errors.php | 4 ++--
lang/de/notifications.php | 4 ++--
lang/de/preferences.php | 2 +-
lang/de_informal/errors.php | 4 ++--
lang/de_informal/notifications.php | 4 ++--
lang/de_informal/preferences.php | 2 +-
lang/fr/settings.php | 2 +-
lang/ja/settings.php | 2 +-
9 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/lang/cs/settings.php b/lang/cs/settings.php
index ef25f1a20..a8c4036e8 100644
--- a/lang/cs/settings.php
+++ b/lang/cs/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Kapitoly jako první',
'sort_rule_op_chapters_last' => 'Kapitoly jako poslední',
'sorting_page_limits' => 'Počet zobrazených položek na stránce',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
+ 'sorting_page_limits_desc' => 'Nastavte, kolik položek se má zobrazit na stránce v různých seznamech na webu. Obvykle bude nižší počet výkonnější, zatímco vyšší počet eliminuje nutnost proklikávat se několika stránkami. Doporučuje se použít násobek čísla 6.',
// Maintenance settings
'maint' => 'Údržba',
diff --git a/lang/de/errors.php b/lang/de/errors.php
index 75c1cf394..ee5071f85 100644
--- a/lang/de/errors.php
+++ b/lang/de/errors.php
@@ -109,7 +109,7 @@ return [
'import_zip_cant_read' => 'ZIP-Datei konnte nicht gelesen werden.',
'import_zip_cant_decode_data' => 'ZIP data.json konnte nicht gefunden und dekodiert werden.',
'import_zip_no_data' => 'ZIP-Datei Daten haben kein erwartetes Buch, Kapitel oder Seiteninhalt.',
- 'import_zip_data_too_large' => 'ZIP data.json content exceeds the configured application maximum upload size.',
+ 'import_zip_data_too_large' => 'Der Inhalt der ZIP data.json überschreitet die maximale Dateigröße der Anwendung.',
'import_validation_failed' => 'ZIP Import konnte mit Fehlern nicht validiert werden:',
'import_zip_failed_notification' => 'Importieren der ZIP-Datei fehlgeschlagen.',
'import_perms_books' => 'Ihnen fehlt die erforderliche Berechtigung, um Bücher zu erstellen.',
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Das Kennwort für das angegebene API-Token ist falsch',
'api_user_no_api_permission' => 'Der Besitzer des verwendeten API-Tokens hat keine Berechtigung für API-Aufrufe',
'api_user_token_expired' => 'Das verwendete Autorisierungstoken ist abgelaufen',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Nur GET Anfragen sind erlaubt, wenn die API mit Cookie-basierter Authentifizierung verwendet wird',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Fehler beim Versenden einer Test E-Mail:',
diff --git a/lang/de/notifications.php b/lang/de/notifications.php
index 71b71d1b6..cd8f4c874 100644
--- a/lang/de/notifications.php
+++ b/lang/de/notifications.php
@@ -11,8 +11,8 @@ return [
'updated_page_subject' => 'Aktualisierte Seite: :pageName',
'updated_page_intro' => 'Eine Seite wurde in :appName aktualisiert:',
'updated_page_debounce' => 'Um eine Flut von Benachrichtigungen zu vermeiden, werden Sie für eine gewisse Zeit keine Benachrichtigungen für weitere Bearbeitungen dieser Seite durch denselben Bearbeiter erhalten.',
- 'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
- 'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
+ 'comment_mention_subject' => 'Sie wurden in einem Kommentar auf der Seite :pageName erwähnt',
+ 'comment_mention_intro' => 'Sie wurden in einem Kommentar zu :appName: erwähnt',
'detail_page_name' => 'Name der Seite:',
'detail_page_path' => 'Seitenpfad:',
diff --git a/lang/de/preferences.php b/lang/de/preferences.php
index 1f74f3d3e..858cf35a3 100644
--- a/lang/de/preferences.php
+++ b/lang/de/preferences.php
@@ -23,7 +23,7 @@ return [
'notifications_desc' => 'Legen Sie fest, welche E-Mail-Benachrichtigungen Sie erhalten, wenn bestimmte Aktivitäten im System durchgeführt werden.',
'notifications_opt_own_page_changes' => 'Benachrichtigung bei Änderungen an eigenen Seiten',
'notifications_opt_own_page_comments' => 'Benachrichtigung bei Kommentaren an eigenen Seiten',
- 'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
+ 'notifications_opt_comment_mentions' => 'Bei Erwähnung mich benachrichtigen',
'notifications_opt_comment_replies' => 'Bei Antworten auf meine Kommentare benachrichtigen',
'notifications_save' => 'Einstellungen speichern',
'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!',
diff --git a/lang/de_informal/errors.php b/lang/de_informal/errors.php
index a3be19a02..f43b4e5ac 100644
--- a/lang/de_informal/errors.php
+++ b/lang/de_informal/errors.php
@@ -109,7 +109,7 @@ return [
'import_zip_cant_read' => 'ZIP-Datei konnte nicht gelesen werden.',
'import_zip_cant_decode_data' => 'Konnte Inhalt der data.json im ZIP nicht finden und dekodieren.',
'import_zip_no_data' => 'ZIP-Datei hat kein erwartetes Buch, Kapitel oder Seiteninhalt.',
- 'import_zip_data_too_large' => 'ZIP data.json content exceeds the configured application maximum upload size.',
+ 'import_zip_data_too_large' => 'Der Inhalt der ZIP data.json überschreitet die maximale Dateigröße der Anwendung.',
'import_validation_failed' => 'ZIP Import konnte aufgrund folgender Fehler nicht validiert werden:',
'import_zip_failed_notification' => 'Importieren der ZIP-Datei fehlgeschlagen.',
'import_perms_books' => 'Dir fehlt die erforderliche Berechtigung, um Bücher zu erstellen.',
@@ -125,7 +125,7 @@ return [
'api_incorrect_token_secret' => 'Das für den API-Token angegebene geheime Token ist falsch',
'api_user_no_api_permission' => 'Der Besitzer des verwendeten API-Token hat keine Berechtigung für API-Aufrufe',
'api_user_token_expired' => 'Das verwendete Autorisierungs-Token ist abgelaufen',
- 'api_cookie_auth_only_get' => 'Only GET requests are allowed when using the API with cookie-based authentication',
+ 'api_cookie_auth_only_get' => 'Nur GET Anfragen sind erlaubt, wenn die API mit Cookie-basierter Authentifizierung verwendet wird',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Fehler beim Senden einer Test E-Mail:',
diff --git a/lang/de_informal/notifications.php b/lang/de_informal/notifications.php
index 99c270ec1..4df170783 100644
--- a/lang/de_informal/notifications.php
+++ b/lang/de_informal/notifications.php
@@ -11,8 +11,8 @@ return [
'updated_page_subject' => 'Aktualisierte Seite: :pageName',
'updated_page_intro' => 'Eine Seite wurde in :appName aktualisiert:',
'updated_page_debounce' => 'Um eine Flut von Benachrichtigungen zu vermeiden, wirst du für eine gewisse Zeit keine Benachrichtigungen für weitere Bearbeitungen dieser Seite durch denselben Bearbeiter erhalten.',
- 'comment_mention_subject' => 'You have been mentioned in a comment on page: :pageName',
- 'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
+ 'comment_mention_subject' => 'Sie wurden in einem Kommentar auf der Seite :pageName erwähnt',
+ 'comment_mention_intro' => 'Sie wurden in einem Kommentar zu :appName: erwähnt',
'detail_page_name' => 'Seitenname:',
'detail_page_path' => 'Seitenpfad:',
diff --git a/lang/de_informal/preferences.php b/lang/de_informal/preferences.php
index bfb57b2f4..443f70b0a 100644
--- a/lang/de_informal/preferences.php
+++ b/lang/de_informal/preferences.php
@@ -23,7 +23,7 @@ return [
'notifications_desc' => 'Lege fest, welche E-Mail-Benachrichtigungen du erhältst, wenn bestimmte Aktivitäten im System durchgeführt werden.',
'notifications_opt_own_page_changes' => 'Benachrichtigung bei Änderungen an eigenen Seiten',
'notifications_opt_own_page_comments' => 'Benachrichtigung bei Kommentaren an eigenen Seiten',
- 'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
+ 'notifications_opt_comment_mentions' => 'Bei Erwähnung mich benachrichtigen',
'notifications_opt_comment_replies' => 'Bei Antworten auf meine Kommentare benachrichtigen',
'notifications_save' => 'Einstellungen speichern',
'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!',
diff --git a/lang/fr/settings.php b/lang/fr/settings.php
index 7b0987a48..8c6c57f33 100644
--- a/lang/fr/settings.php
+++ b/lang/fr/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'Chapitres en premier',
'sort_rule_op_chapters_last' => 'Chapitres en dernier',
'sorting_page_limits' => 'Limite d\'affichage par page',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
+ 'sorting_page_limits_desc' => 'Définissez le nombre d’éléments à afficher par page dans les différentes listes du système. En général, un nombre plus faible offre de meilleures performances, tandis qu’un nombre plus élevé réduit le besoin de naviguer entre plusieurs pages. Il est recommandé d’utiliser un multiple de 6.',
// Maintenance settings
'maint' => 'Maintenance',
diff --git a/lang/ja/settings.php b/lang/ja/settings.php
index 717a4c10f..378e3a774 100644
--- a/lang/ja/settings.php
+++ b/lang/ja/settings.php
@@ -104,7 +104,7 @@ return [
'sort_rule_op_chapters_first' => 'チャプタを最初に',
'sort_rule_op_chapters_last' => 'チャプタを最後に',
'sorting_page_limits' => 'ページング表示制限',
- 'sorting_page_limits_desc' => 'Set how many items to show per-page in various lists within the system. Typically a lower amount will be more performant, while a higher amount avoids the need to click through multiple pages. Using a multiple of 6 is recommended.',
+ 'sorting_page_limits_desc' => 'システム内の各種リストで1ページに表示するアイテム数を設定します。 通常、少ない数に設定するとパフォーマンスが向上し、多い数に設定するとページの移動操作が少なくなります。6 の倍数に設定することをお勧めします。',
// Maintenance settings
'maint' => 'メンテナンス',
From 1763ac550b3dbc7339cbdbb9791253bcddbaf67a Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Mon, 23 Mar 2026 10:08:38 +0000
Subject: [PATCH 18/85] Meta: Updated translators pre v26.03.2 release
---
.github/translators.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/translators.txt b/.github/translators.txt
index fcbb9675d..97ab6c3fc 100644
--- a/.github/translators.txt
+++ b/.github/translators.txt
@@ -533,3 +533,4 @@ JanDziaslo :: Polish
Charllys Fernandes (CharllysFernandes) :: Portuguese, Brazilian
Ilgiz Zigangirov (inov8) :: Russian
Max Israelsson (Blezie) :: Swedish
+Skiddybison5924 (chris-devel0per) :: German
From 0b659671fe08da3a699f0b4ddd93e77e18d13b11 Mon Sep 17 00:00:00 2001
From: ololukaszuk <47779810+ololukaszuk@users.noreply.github.com>
Date: Wed, 25 Mar 2026 15:23:15 +0100
Subject: [PATCH 19/85] Fix PDF heading font fallback for export
---
resources/sass/export-styles.scss | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/resources/sass/export-styles.scss b/resources/sass/export-styles.scss
index 8dd7be375..22f15d1b8 100644
--- a/resources/sass/export-styles.scss
+++ b/resources/sass/export-styles.scss
@@ -64,6 +64,11 @@ body.export-format-pdf {
font-size: 14px;
line-height: 1.2;
+ // Ensure heading glyph coverage for PDF engines that don't handle CSS vars well.
+ h1, h2, h3, h4, h5, h6 {
+ font-family: 'DejaVu Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+ }
+
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
}
@@ -100,4 +105,4 @@ body.export-format-pdf.export-engine-dompdf {
.page-content td a > img {
max-width: 100%;
}
-}
\ No newline at end of file
+}
From c7d3775bb999bda0d134de900b28f2ebeb4cec67 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 5 Apr 2026 00:05:10 +0100
Subject: [PATCH 20/85] Plain text: Created a new HTML to plain text converter
To centralise logic to be more consistent, and to have smarter logic
which avoids just following newline format from input, preventing
smushing HTML elements (like list elements) next to eachother
---
app/Activity/Models/Comment.php | 7 +++
.../Messages/CommentCreationNotification.php | 2 +-
.../Messages/CommentMentionNotification.php | 2 +-
app/Entities/Repos/BaseRepo.php | 4 +-
app/Entities/Tools/PageContent.php | 5 +-
app/Util/HtmlToPlainText.php | 47 ++++++++++++++
tests/Util/HtmlToPlainTextTest.php | 63 +++++++++++++++++++
7 files changed, 125 insertions(+), 5 deletions(-)
create mode 100644 app/Util/HtmlToPlainText.php
create mode 100644 tests/Util/HtmlToPlainTextTest.php
diff --git a/app/Activity/Models/Comment.php b/app/Activity/Models/Comment.php
index ab7d91772..3faa76657 100644
--- a/app/Activity/Models/Comment.php
+++ b/app/Activity/Models/Comment.php
@@ -9,6 +9,7 @@ use BookStack\Users\Models\HasCreatorAndUpdater;
use BookStack\Users\Models\OwnableInterface;
use BookStack\Util\HtmlContentFilter;
use BookStack\Util\HtmlContentFilterConfig;
+use BookStack\Util\HtmlToPlainText;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -87,6 +88,12 @@ class Comment extends Model implements Loggable, OwnableInterface
return $filter->filterString($this->html ?? '');
}
+ public function getPlainText(): string
+ {
+ $converter = new HtmlToPlainText();
+ return $converter->convert($this->html ?? '');
+ }
+
public function jointPermissions(): HasMany
{
return $this->hasMany(JointPermission::class, 'entity_id', 'commentable_id')
diff --git a/app/Activity/Notifications/Messages/CommentCreationNotification.php b/app/Activity/Notifications/Messages/CommentCreationNotification.php
index 30d0ffa2b..d739f4aab 100644
--- a/app/Activity/Notifications/Messages/CommentCreationNotification.php
+++ b/app/Activity/Notifications/Messages/CommentCreationNotification.php
@@ -24,7 +24,7 @@ class CommentCreationNotification extends BaseActivityNotification
$locale->trans('notifications.detail_page_name') => new EntityLinkMessageLine($page),
$locale->trans('notifications.detail_page_path') => $this->buildPagePathLine($page, $notifiable),
$locale->trans('notifications.detail_commenter') => $this->user->name,
- $locale->trans('notifications.detail_comment') => strip_tags($comment->html),
+ $locale->trans('notifications.detail_comment') => $comment->getPlainText(),
]);
return $this->newMailMessage($locale)
diff --git a/app/Activity/Notifications/Messages/CommentMentionNotification.php b/app/Activity/Notifications/Messages/CommentMentionNotification.php
index de9e71963..4c8ee5bab 100644
--- a/app/Activity/Notifications/Messages/CommentMentionNotification.php
+++ b/app/Activity/Notifications/Messages/CommentMentionNotification.php
@@ -24,7 +24,7 @@ class CommentMentionNotification extends BaseActivityNotification
$locale->trans('notifications.detail_page_name') => new EntityLinkMessageLine($page),
$locale->trans('notifications.detail_page_path') => $this->buildPagePathLine($page, $notifiable),
$locale->trans('notifications.detail_commenter') => $this->user->name,
- $locale->trans('notifications.detail_comment') => strip_tags($comment->html),
+ $locale->trans('notifications.detail_comment') => $comment->getPlainText(),
]);
return $this->newMailMessage($locale)
diff --git a/app/Entities/Repos/BaseRepo.php b/app/Entities/Repos/BaseRepo.php
index 717e9c9f8..44baeaccf 100644
--- a/app/Entities/Repos/BaseRepo.php
+++ b/app/Entities/Repos/BaseRepo.php
@@ -16,6 +16,7 @@ use BookStack\References\ReferenceUpdater;
use BookStack\Sorting\BookSorter;
use BookStack\Uploads\ImageRepo;
use BookStack\Util\HtmlDescriptionFilter;
+use BookStack\Util\HtmlToPlainText;
use Illuminate\Http\UploadedFile;
class BaseRepo
@@ -151,9 +152,10 @@ class BaseRepo
}
if (isset($input['description_html'])) {
+ $plainTextConverter = new HtmlToPlainText();
$entity->descriptionInfo()->set(
HtmlDescriptionFilter::filterFromString($input['description_html']),
- html_entity_decode(strip_tags($input['description_html']))
+ $plainTextConverter->convert($input['description_html']),
);
} else if (isset($input['description'])) {
$entity->descriptionInfo()->set('', $input['description']);
diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php
index 8d89a86cf..b86fbbe8b 100644
--- a/app/Entities/Tools/PageContent.php
+++ b/app/Entities/Tools/PageContent.php
@@ -16,6 +16,7 @@ use BookStack\Users\Models\User;
use BookStack\Util\HtmlContentFilter;
use BookStack\Util\HtmlContentFilterConfig;
use BookStack\Util\HtmlDocument;
+use BookStack\Util\HtmlToPlainText;
use BookStack\Util\WebSafeMimeSniffer;
use Closure;
use DOMElement;
@@ -303,8 +304,8 @@ class PageContent
public function toPlainText(): string
{
$html = $this->render(true);
-
- return html_entity_decode(strip_tags($html));
+ $converter = new HtmlToPlainText();
+ return $converter->convert($html);
}
/**
diff --git a/app/Util/HtmlToPlainText.php b/app/Util/HtmlToPlainText.php
new file mode 100644
index 000000000..79da9e3d8
--- /dev/null
+++ b/app/Util/HtmlToPlainText.php
@@ -0,0 +1,47 @@
+nodeToText($doc->getBody());
+
+ // Remove repeated newlines
+ $text = preg_replace('/\n+/', "\n", $text);
+ // Remove leading/trailing whitespace
+ $text = trim($text);
+
+ return $text;
+ }
+
+ protected function nodeToText(\DOMNode $node): string
+ {
+ if ($node->nodeType === XML_TEXT_NODE) {
+ return $node->textContent;
+ }
+
+ $text = '';
+ if (!in_array($node->nodeName, $this->inlineTags)) {
+ $text .= "\n";
+ }
+
+ foreach ($node->childNodes as $childNode) {
+ $text .= $this->nodeToText($childNode);
+ }
+
+ return $text;
+ }
+}
diff --git a/tests/Util/HtmlToPlainTextTest.php b/tests/Util/HtmlToPlainTextTest.php
new file mode 100644
index 000000000..e522e4863
--- /dev/null
+++ b/tests/Util/HtmlToPlainTextTest.php
@@ -0,0 +1,63 @@
+This is a test
diff --git a/tests/Sorting/BookSortTest.php b/tests/Sorting/BookSortTest.php
index 7f31f9c27..33a10609a 100644
--- a/tests/Sorting/BookSortTest.php
+++ b/tests/Sorting/BookSortTest.php
@@ -271,6 +271,21 @@ class BookSortTest extends TestCase
$this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"]');
}
+ public function test_auto_sort_rule_create_hint_shown_on_sort_page()
+ {
+ $book = $this->entities->book();
+ $hintText = 'Auto sort option rules can be created in the "Lists & Sorting" settings area by a user with the relevant permissions.';
+
+ // Admin users see link for creating new rule
+ $resp = $this->asAdmin()->get($book->getUrl('/sort'));
+ $this->withHtml($resp)->assertLinkExists(url('/settings/sorting/rules/new'), 'Create Sort Rule');
+ $resp->assertDontSee($hintText);
+
+ // Non-admin users see help text
+ $resp = $this->asEditor()->get($book->getUrl('/sort'));
+ $resp->assertSee($hintText);
+ }
+
public function test_auto_sort_option_submit_saves_to_book()
{
$sort = SortRule::factory()->create();
From c7e2b487c14133ef3fa62321fb226501a0f3fc88 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 12 Apr 2026 15:17:31 +0100
Subject: [PATCH 31/85] Attachments: Aligned ZipExportAttachment link
validation
With controller routes.
Don't consider this as a security issue, since the filtered URLs
by that validation are very likely to be blocked by browser security
or CSP, and there's a level of assumed privilege to the users that
are able to create such attachments links already.
Closes #6093
---
.../ZipExports/Models/ZipExportAttachment.php | 2 +-
tests/Exports/ZipExportValidatorTest.php | 25 +++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/app/Exports/ZipExports/Models/ZipExportAttachment.php b/app/Exports/ZipExports/Models/ZipExportAttachment.php
index 97995738f..88c20e4d3 100644
--- a/app/Exports/ZipExports/Models/ZipExportAttachment.php
+++ b/app/Exports/ZipExports/Models/ZipExportAttachment.php
@@ -45,7 +45,7 @@ final class ZipExportAttachment extends ZipExportModel
$rules = [
'id' => ['nullable', 'int', $context->uniqueIdRule('attachment')],
'name' => ['required', 'string', 'min:1'],
- 'link' => ['required_without:file', 'nullable', 'string'],
+ 'link' => ['required_without:file', 'nullable', 'string', 'safe_url'],
'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()],
];
diff --git a/tests/Exports/ZipExportValidatorTest.php b/tests/Exports/ZipExportValidatorTest.php
index c453ef294..e801705be 100644
--- a/tests/Exports/ZipExportValidatorTest.php
+++ b/tests/Exports/ZipExportValidatorTest.php
@@ -90,4 +90,29 @@ class ZipExportValidatorTest extends TestCase
$this->assertEquals('The file needs to reference a file of type image/png,image/jpeg,image/gif,image/webp, found text/plain.', $results['page.images.0.file']);
}
+
+ public function test_page_link_attachments_cant_be_data_or_js()
+ {
+ $validateResultCountByLink = [
+ 'data:text/html,
hi
' => 1,
+ 'javascript:alert(\'hi\')' => 1,
+ 'mailto:email@example.com' => 0,
+ ];
+
+ foreach ($validateResultCountByLink as $link => $count) {
+ $validator = $this->getValidatorForData([
+ 'page' => [
+ 'id' => 4,
+ 'name' => 'My page',
+ 'markdown' => 'hello',
+ 'attachments' => [
+ ['id' => 4, 'name' => 'Attachment A', 'link' => $link],
+ ],
+ ]
+ ]);
+
+ $results = $validator->validate();
+ $this->assertCount($count, $results);
+ }
+ }
}
From 4feb50e7ee06206b2ef037090673cabb70a17c73 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 12 Apr 2026 15:29:00 +0100
Subject: [PATCH 32/85] Attachments: Aligned attachment validation a little
more
---
app/Exports/ZipExports/Models/ZipExportAttachment.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/Exports/ZipExports/Models/ZipExportAttachment.php b/app/Exports/ZipExports/Models/ZipExportAttachment.php
index 88c20e4d3..161186687 100644
--- a/app/Exports/ZipExports/Models/ZipExportAttachment.php
+++ b/app/Exports/ZipExports/Models/ZipExportAttachment.php
@@ -45,7 +45,7 @@ final class ZipExportAttachment extends ZipExportModel
$rules = [
'id' => ['nullable', 'int', $context->uniqueIdRule('attachment')],
'name' => ['required', 'string', 'min:1'],
- 'link' => ['required_without:file', 'nullable', 'string', 'safe_url'],
+ 'link' => ['required_without:file', 'nullable', 'string', 'max:2000', 'safe_url'],
'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()],
];
From f14fc68b6697c203f4012c5bf537dcb49db85f87 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 12 Apr 2026 18:26:00 +0100
Subject: [PATCH 33/85] API: Added new tags API endpoints
---
app/Activity/Controllers/TagApiController.php | 51 +++++++++++++++++++
app/Activity/Controllers/TagController.php | 4 +-
app/Activity/TagRepo.php | 34 ++++++++++---
app/Api/ListingResponseBuilder.php | 32 +++++++++---
app/Http/ApiController.php | 6 ++-
routes/api.php | 4 ++
6 files changed, 112 insertions(+), 19 deletions(-)
create mode 100644 app/Activity/Controllers/TagApiController.php
diff --git a/app/Activity/Controllers/TagApiController.php b/app/Activity/Controllers/TagApiController.php
new file mode 100644
index 000000000..c1945ba5c
--- /dev/null
+++ b/app/Activity/Controllers/TagApiController.php
@@ -0,0 +1,51 @@
+tagRepo
+ ->queryWithTotalsForApi('');
+
+ return $this->apiListingResponse($tagQuery, [
+ 'name', 'values', 'usages', 'page_count', 'chapter_count', 'book_count', 'shelf_count',
+ ], [], [
+ 'name'
+ ]);
+ }
+
+ /**
+ * Get a list of tag values used in the system, which have been used for the given tag name.
+ * You'll only see results based on tags applied to content you have access to.
+ * Only the value field can be used in filters.
+ */
+ public function listValues(string $name): JsonResponse
+ {
+ $tagQuery = $this->tagRepo
+ ->queryWithTotalsForApi($name);
+
+ return $this->apiListingResponse($tagQuery, [
+ 'name', 'value', 'usages', 'page_count', 'chapter_count', 'book_count', 'shelf_count',
+ ], [], [
+ 'name', 'value',
+ ]);
+ }
+}
diff --git a/app/Activity/Controllers/TagController.php b/app/Activity/Controllers/TagController.php
index 0af8835ca..723dc4ab4 100644
--- a/app/Activity/Controllers/TagController.php
+++ b/app/Activity/Controllers/TagController.php
@@ -24,9 +24,9 @@ class TagController extends Controller
'usages' => trans('entities.tags_usages'),
]);
- $nameFilter = $request->get('name', '');
+ $nameFilter = $request->input('name', '');
$tags = $this->tagRepo
- ->queryWithTotals($listOptions, $nameFilter)
+ ->queryWithTotalsForList($listOptions, $nameFilter)
->paginate(50)
->appends(array_filter(array_merge($listOptions->getPaginationAppends(), [
'name' => $nameFilter,
diff --git a/app/Activity/TagRepo.php b/app/Activity/TagRepo.php
index 82c26b00e..3e8d5545a 100644
--- a/app/Activity/TagRepo.php
+++ b/app/Activity/TagRepo.php
@@ -18,9 +18,10 @@ class TagRepo
}
/**
- * Start a query against all tags in the system.
+ * Start a query against all tags in the system, with total counts for their usage,
+ * suitable for a system interface list with listing options.
*/
- public function queryWithTotals(SimpleListOptions $listOptions, string $nameFilter): Builder
+ public function queryWithTotalsForList(SimpleListOptions $listOptions, string $nameFilter): Builder
{
$searchTerm = $listOptions->getSearch();
$sort = $listOptions->getSort();
@@ -28,17 +29,34 @@ class TagRepo
$sort = 'value';
}
+ $query = $this->baseQueryWithTotals($nameFilter, $searchTerm)
+ ->orderBy($sort, $listOptions->getOrder());
+
+ return $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
+ }
+
+ /**
+ * Start a query against all tags in the system, with total counts for their usage,
+ * which can be used via the API.
+ */
+ public function queryWithTotalsForApi(string $nameFilter): Builder
+ {
+ $query = $this->baseQueryWithTotals($nameFilter, '');
+ return $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
+ }
+
+ protected function baseQueryWithTotals(string $nameFilter, string $searchTerm): Builder
+ {
$query = Tag::query()
->select([
'name',
($searchTerm || $nameFilter) ? 'value' : DB::raw('COUNT(distinct value) as `values`'),
DB::raw('COUNT(id) as usages'),
- DB::raw('SUM(IF(entity_type = \'page\', 1, 0)) as page_count'),
- DB::raw('SUM(IF(entity_type = \'chapter\', 1, 0)) as chapter_count'),
- DB::raw('SUM(IF(entity_type = \'book\', 1, 0)) as book_count'),
- DB::raw('SUM(IF(entity_type = \'bookshelf\', 1, 0)) as shelf_count'),
+ DB::raw('CAST(SUM(IF(entity_type = \'page\', 1, 0)) as UNSIGNED) as page_count'),
+ DB::raw('CAST(SUM(IF(entity_type = \'chapter\', 1, 0)) as UNSIGNED) as chapter_count'),
+ DB::raw('CAST(SUM(IF(entity_type = \'book\', 1, 0)) as UNSIGNED) as book_count'),
+ DB::raw('CAST(SUM(IF(entity_type = \'bookshelf\', 1, 0)) as UNSIGNED) as shelf_count'),
])
- ->orderBy($sort, $listOptions->getOrder())
->whereHas('entity');
if ($nameFilter) {
@@ -57,7 +75,7 @@ class TagRepo
});
}
- return $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
+ return $query;
}
/**
diff --git a/app/Api/ListingResponseBuilder.php b/app/Api/ListingResponseBuilder.php
index 44117bad9..6b9cfdd7d 100644
--- a/app/Api/ListingResponseBuilder.php
+++ b/app/Api/ListingResponseBuilder.php
@@ -18,6 +18,13 @@ class ListingResponseBuilder
*/
protected array $fields;
+ /**
+ * Which fields are filterable.
+ * When null, the $fields above are used instead (Allow all fields).
+ * @var string[]|null
+ */
+ protected array|null $filterableFields = null;
+
/**
* @var array
*/
@@ -54,7 +61,7 @@ class ListingResponseBuilder
{
$filteredQuery = $this->filterQuery($this->query);
- $total = $filteredQuery->count();
+ $total = $filteredQuery->getCountForPagination();
$data = $this->fetchData($filteredQuery)->each(function ($model) {
foreach ($this->resultModifiers as $modifier) {
$modifier($model);
@@ -77,6 +84,14 @@ class ListingResponseBuilder
$this->resultModifiers[] = $modifier;
}
+ /**
+ * Limit filtering to just the given set of fields.
+ */
+ public function setFilterableFields(array $fields): void
+ {
+ $this->filterableFields = $fields;
+ }
+
/**
* Fetch the data to return within the response.
*/
@@ -94,7 +109,7 @@ class ListingResponseBuilder
protected function filterQuery(Builder $query): Builder
{
$query = clone $query;
- $requestFilters = $this->request->get('filter', []);
+ $requestFilters = $this->request->input('filter', []);
if (!is_array($requestFilters)) {
return $query;
}
@@ -114,10 +129,11 @@ class ListingResponseBuilder
protected function requestFilterToQueryFilter($fieldKey, $value): ?array
{
$splitKey = explode(':', $fieldKey);
- $field = $splitKey[0];
+ $field = strtolower($splitKey[0]);
$filterOperator = $splitKey[1] ?? 'eq';
- if (!in_array($field, $this->fields)) {
+ $filterFields = $this->filterableFields ?? $this->fields;
+ if (!in_array($field, $filterFields)) {
return null;
}
@@ -140,8 +156,8 @@ class ListingResponseBuilder
$defaultSortName = $this->fields[0];
$direction = 'asc';
- $sort = $this->request->get('sort', '');
- if (strpos($sort, '-') === 0) {
+ $sort = $this->request->input('sort', '');
+ if (str_starts_with($sort, '-')) {
$direction = 'desc';
}
@@ -160,9 +176,9 @@ class ListingResponseBuilder
protected function countAndOffsetQuery(Builder $query): Builder
{
$query = clone $query;
- $offset = max(0, $this->request->get('offset', 0));
+ $offset = max(0, $this->request->input('offset', 0));
$maxCount = config('api.max_item_count');
- $count = $this->request->get('count', config('api.default_item_count'));
+ $count = $this->request->input('count', config('api.default_item_count'));
$count = max(min($maxCount, $count), 1);
return $query->skip($offset)->take($count);
diff --git a/app/Http/ApiController.php b/app/Http/ApiController.php
index 8c0f206d0..f1b74783f 100644
--- a/app/Http/ApiController.php
+++ b/app/Http/ApiController.php
@@ -20,10 +20,14 @@ abstract class ApiController extends Controller
* Provide a paginated listing JSON response in a standard format
* taking into account any pagination parameters passed by the user.
*/
- protected function apiListingResponse(Builder $query, array $fields, array $modifiers = []): JsonResponse
+ protected function apiListingResponse(Builder $query, array $fields, array $modifiers = [], array $filterableFields = []): JsonResponse
{
$listing = new ListingResponseBuilder($query, request(), $fields);
+ if (count($filterableFields) > 0) {
+ $listing->setFilterableFields($filterableFields);
+ }
+
foreach ($modifiers as $modifier) {
$listing->modifyResults($modifier);
}
diff --git a/routes/api.php b/routes/api.php
index 308a95d8c..9f45cefb9 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -7,6 +7,7 @@
*/
use BookStack\Activity\Controllers as ActivityControllers;
+use BookStack\Activity\Controllers\TagApiController;
use BookStack\Api\ApiDocsController;
use BookStack\App\SystemApiController;
use BookStack\Entities\Controllers as EntityControllers;
@@ -109,6 +110,9 @@ Route::get('search', [SearchApiController::class, 'all']);
Route::get('system', [SystemApiController::class, 'read']);
+Route::get('tags/names', [TagApiController::class, 'listNames']);
+Route::get('tags/name/{name}/values', [TagApiController::class, 'listValues']);
+
Route::get('users', [UserApiController::class, 'list']);
Route::post('users', [UserApiController::class, 'create']);
Route::get('users/{id}', [UserApiController::class, 'read']);
From 1c1ad1d1b739d2558d31478610b1ca4af553f3eb Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sun, 12 Apr 2026 20:45:18 +0100
Subject: [PATCH 34/85] Tags API: Reviewed docs and added examples
---
app/Activity/Controllers/TagApiController.php | 10 ++++--
app/Api/ApiDocsGenerator.php | 7 ++--
dev/api/responses/tags-list-names.json | 32 +++++++++++++++++++
dev/api/responses/tags-list-values.json | 32 +++++++++++++++++++
.../views/api-docs/parts/endpoint.blade.php | 2 +-
.../api-docs/parts/getting-started.blade.php | 2 +-
6 files changed, 77 insertions(+), 8 deletions(-)
create mode 100644 dev/api/responses/tags-list-names.json
create mode 100644 dev/api/responses/tags-list-values.json
diff --git a/app/Activity/Controllers/TagApiController.php b/app/Activity/Controllers/TagApiController.php
index c1945ba5c..1fdffa007 100644
--- a/app/Activity/Controllers/TagApiController.php
+++ b/app/Activity/Controllers/TagApiController.php
@@ -8,6 +8,12 @@ use BookStack\Activity\TagRepo;
use BookStack\Http\ApiController;
use Illuminate\Http\JsonResponse;
+/**
+ * Endpoints to query data about tags in the system.
+ * You'll only see results based on tags applied to content you have access to.
+ * There are no general create/update/delete endpoints here since tags do not exist
+ * by themselves, they are managed via the items they are assigned to.
+ */
class TagApiController extends ApiController
{
public function __construct(
@@ -17,7 +23,6 @@ class TagApiController extends ApiController
/**
* Get a list of tag names used in the system.
- * You'll only see results based on tags applied to content you have access to.
* Only the name field can be used in filters.
*/
public function listNames(): JsonResponse
@@ -33,8 +38,7 @@ class TagApiController extends ApiController
}
/**
- * Get a list of tag values used in the system, which have been used for the given tag name.
- * You'll only see results based on tags applied to content you have access to.
+ * Get a list of tag values, which have been set for the given tag name.
* Only the value field can be used in filters.
*/
public function listValues(string $name): JsonResponse
diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php
index a59cb8198..53cb2890a 100644
--- a/app/Api/ApiDocsGenerator.php
+++ b/app/Api/ApiDocsGenerator.php
@@ -195,11 +195,12 @@ class ApiDocsGenerator
protected function getFlatApiRoutes(): Collection
{
return collect(Route::getRoutes()->getRoutes())->filter(function ($route) {
- return strpos($route->uri, 'api/') === 0;
+ return str_starts_with($route->uri, 'api/');
})->map(function ($route) {
[$controller, $controllerMethod] = explode('@', $route->action['uses']);
$baseModelName = explode('.', explode('/', $route->uri)[1])[0];
- $shortName = $baseModelName . '-' . $controllerMethod;
+ $controllerMethodKebab = Str::kebab($controllerMethod);
+ $shortName = $baseModelName . '-' . $controllerMethodKebab;
return [
'name' => $shortName,
@@ -207,7 +208,7 @@ class ApiDocsGenerator
'method' => $route->methods[0],
'controller' => $controller,
'controller_method' => $controllerMethod,
- 'controller_method_kebab' => Str::kebab($controllerMethod),
+ 'controller_method_kebab' => $controllerMethodKebab,
'base_model' => $baseModelName,
];
});
diff --git a/dev/api/responses/tags-list-names.json b/dev/api/responses/tags-list-names.json
new file mode 100644
index 000000000..c0c8e7b22
--- /dev/null
+++ b/dev/api/responses/tags-list-names.json
@@ -0,0 +1,32 @@
+{
+ "data": [
+ {
+ "name": "Category",
+ "values": 8,
+ "usages": 184,
+ "page_count": 3,
+ "chapter_count": 8,
+ "book_count": 171,
+ "shelf_count": 2
+ },
+ {
+ "name": "Review Due",
+ "values": 2,
+ "usages": 2,
+ "page_count": 1,
+ "chapter_count": 0,
+ "book_count": 1,
+ "shelf_count": 0
+ },
+ {
+ "name": "Type",
+ "values": 2,
+ "usages": 2,
+ "page_count": 0,
+ "chapter_count": 1,
+ "book_count": 1,
+ "shelf_count": 0
+ }
+ ],
+ "total": 3
+}
\ No newline at end of file
diff --git a/dev/api/responses/tags-list-values.json b/dev/api/responses/tags-list-values.json
new file mode 100644
index 000000000..37926b846
--- /dev/null
+++ b/dev/api/responses/tags-list-values.json
@@ -0,0 +1,32 @@
+{
+ "data": [
+ {
+ "name": "Category",
+ "value": "Cool Stuff",
+ "usages": 3,
+ "page_count": 1,
+ "chapter_count": 0,
+ "book_count": 2,
+ "shelf_count": 0
+ },
+ {
+ "name": "Category",
+ "value": "Top Content",
+ "usages": 168,
+ "page_count": 0,
+ "chapter_count": 3,
+ "book_count": 165,
+ "shelf_count": 0
+ },
+ {
+ "name": "Category",
+ "value": "Learning",
+ "usages": 2,
+ "page_count": 0,
+ "chapter_count": 0,
+ "book_count": 0,
+ "shelf_count": 2
+ }
+ ],
+ "total": 3
+}
\ No newline at end of file
diff --git a/resources/views/api-docs/parts/endpoint.blade.php b/resources/views/api-docs/parts/endpoint.blade.php
index 024a5ecdf..543ef092e 100644
--- a/resources/views/api-docs/parts/endpoint.blade.php
+++ b/resources/views/api-docs/parts/endpoint.blade.php
@@ -1,7 +1,7 @@