mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
508cf0ade6 | ||
|
|
851aba228a | ||
|
|
25790fd024 | ||
|
|
1763ac550b | ||
|
|
fd6867e577 | ||
|
|
5ebc1fe3b0 | ||
|
|
a44756168d | ||
|
|
fa1dc162bd | ||
|
|
5763d26b17 | ||
|
|
04dd9f8e19 | ||
|
|
7111e080c1 | ||
|
|
ee4786f83a | ||
|
|
0120b475eb | ||
|
|
8a59895ba0 | ||
|
|
a9ffd3e0c7 | ||
|
|
f4c9d2b049 |
1
.github/translators.txt
vendored
1
.github/translators.txt
vendored
@@ -533,3 +533,4 @@ JanDziaslo :: Polish
|
||||
Charllys Fernandes (CharllysFernandes) :: Portuguese, Brazilian
|
||||
Ilgiz Zigangirov (inov8) :: Russian
|
||||
Max Israelsson (Blezie) :: Swedish
|
||||
Skiddybison5924 (chris-devel0per) :: German
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
}
|
||||
|
||||
118
composer.lock
generated
118
composer.lock
generated
@@ -62,16 +62,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.373.2",
|
||||
"version": "3.373.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "483fba51c28b3a0c0647bf5100e0edca82090b18"
|
||||
"reference": "4402bd10f913e66b7271f44466be8d5ba6c9146e"
|
||||
},
|
||||
"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/4402bd10f913e66b7271f44466be8d5ba6c9146e",
|
||||
"reference": "4402bd10f913e66b7271f44466be8d5ba6c9146e",
|
||||
"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.7"
|
||||
},
|
||||
"time": "2026-03-13T18:08:30+00:00"
|
||||
"time": "2026-03-20T18:14:19+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",
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
@@ -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.40",
|
||||
"version": "2.1.42",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
|
||||
"reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0",
|
||||
"reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9218,7 +9218,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-02-23T15:04:35+00:00"
|
||||
"time": "2026-03-17T14:58:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
||||
@@ -1 +1 @@
|
||||
15574ddf174d8f2b0efbdcbc6dbfcd907eb41593821d4f06d47e74822a80a394
|
||||
e23b5dd20fcaa1ba71ecc9e992c96003c73d7c22d85b024003bca3f9b7ac25d0
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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:',
|
||||
|
||||
@@ -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:',
|
||||
|
||||
@@ -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!',
|
||||
|
||||
@@ -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:',
|
||||
|
||||
@@ -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:',
|
||||
|
||||
@@ -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!',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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' => 'メンテナンス',
|
||||
|
||||
2
public/dist/app.js
vendored
2
public/dist/app.js
vendored
File diff suppressed because one or more lines are too long
34
public/dist/wysiwyg.js
vendored
34
public/dist/wysiwyg.js
vendored
File diff suppressed because one or more lines are too long
@@ -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)_
|
||||
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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<T1, T2> = Omit<T2, keyof T1> & 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -221,7 +221,7 @@ export const detailsEditLabel: EditorButtonDefinition = {
|
||||
if ($isDetailsNode(details)) {
|
||||
$showDetailsForm(details, context);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection | null): boolean {
|
||||
return false;
|
||||
|
||||
@@ -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<EditorDecoratorAdapter> = (decorators: Record<NodeKey, EditorDecoratorAdapter>) => {
|
||||
editor.getEditorState().read(() => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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 */
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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', '<html></html><p>testing</p>', '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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user